[CEOI2016] kangaroo(排列dp)
生活随笔
收集整理的這篇文章主要介紹了
[CEOI2016] kangaroo(排列dp)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
problem
luogu-P5999
solution
每個點只能跳一次,顯然跳出來形成的順序是一個排列。不難聯想到最近刷的排列 dpdpdp。
然后,我覺得難點在于怎么轉化這個跳的規則,因為現在并不確定能否按排列 dpdpdp 一樣分段做。
跳的順序形成的排列必須滿足以下兩個條件之一:
- 排列中第 iii 個元素必須小于兩邊的元素。
- 排列中第 iii 個元素必須大于兩邊的元素。
排列的起終點(墻)為 s,ts,ts,t。
轉化后發現,這是可以排列 dpdpdp 的。
我們從小到大考慮 iii。設 f(i,j):f(i,j):f(i,j): 前 iii 個數將排列分成了 jjj 段的合法方案數。
-
iii 不是墻。
-
合并兩段,jjj 段有 j?1j-1j?1 個空。f(i,j)←f(i?1,j+1)?jf(i,j)\leftarrow f(i-1,j+1)*jf(i,j)←f(i?1,j+1)?j。
-
新插一段,可以插在任何位置,但若 sss 已過則不能插首,若 ttt 已過則不能插尾。
f(i,j)←f(i?1,j?1)?(j?(i>s)?(i>t))f(i,j)\leftarrow f(i-1,j-1)*\big(j-(i>s)-(i>t)\big)f(i,j)←f(i?1,j?1)?(j?(i>s)?(i>t))。
-
-
iii 是墻。只能放在首尾。
- 單獨成段。f(i,j)←f(i?1,j?1)f(i,j)\leftarrow f(i-1,j-1)f(i,j)←f(i?1,j?1)。
- 與相鄰段合并,相當于延伸。f(i,j)←f(i?1,j)f(i,j)\leftarrow f(i-1,j)f(i,j)←f(i?1,j)。
時間復雜度 O(n2)O(n^2)O(n2)。
code
#include <bits/stdc++.h> using namespace std; #define int long long #define mod 1000000007 #define maxn 2005 int f[maxn][maxn]; int n, s, t;signed main() {scanf( "%lld %lld %lld", &n, &s, &t );f[1][1] = 1;for( int i = 2;i <= n;i ++ )if( i ^ s and i ^ t )for( int j = 1;j <= n;j ++ )f[i][j] = (f[i - 1][j - 1] * (j - (i > s) - (i > t) ) + f[i - 1][j + 1] * j) % mod;elsefor( int j = 1;j <= n;j ++ )f[i][j] = (f[i - 1][j - 1] + f[i - 1][j]) % mod; printf( "%lld\n", f[n][1] );return 0; }總結
以上是生活随笔為你收集整理的[CEOI2016] kangaroo(排列dp)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【刷题记录】排列dp
- 下一篇: [CF1368E] Ski Accide