【dp】codeforces.R175E. Positions in Permutations
http://codeforces.com/contest/285/problem/E
询问有多少个长度为n的1~n的排列,有且仅有K个位置满足abs(p[i]-i)==1.
神级dp....表示比赛中我只想到了O(n^3)的dp,然后以为无效状态很多,加上cf神机 会给我祝福就直接写了,写完发现本地最大数据要跑5分钟=。=~。。真是太暴力了。。大致dp是这样的。。dp[i][j][k][msk]表示前i个位置,有且仅有j个满足上列条件的位置,且i+2~n之间的数字已经出现了k个,同时用msk一个2位2进制数表示数字i和数字i+1是否已经出现过。。。非常长的一个状态表示,当然转移很简单,按k个i+2~n之间的数字中是否存在数字i+2分成两种情形,每种情形i+1这个位置分别考虑填上 i , i+1,i+2,以及大于i+2或者小于i等5类数字。。具体转移方程有兴趣请参考第一份代码,复杂度方面O(n^3)状态,O(1)转移,再加上滚动数组做到O(n^2)空间复杂度。。
1 //By Lin 2 #include<cstdio> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #include<set> 7 #include<vector> 8 #include<map> 9 #include<queue> 10 #include<cctype> 11 #include<cmath> 12 13 #define eps 1e-9 14 #define N 1010 15 #define sqr(x) ((x)*(x)) 16 #define Rep(i,n) for(int i = 0; i<n; i++) 17 #define foreach(i,n) for( __typeof(n.begin()) i = n.begin(); i!=n.end(); i++) 18 #define X first 19 #define Y second 20 #define mp(x,y) make_pair(x,y) 21 22 using namespace std; 23 typedef long long LL; 24 typedef pair<int,int> pii; 25 26 //int n,data[N],ans[N]; 27 #define MOD 1000000007 28 29 int n,K; 30 int dp[2][N][N][4]; 31 bool mark[2][N][N][4]; 32 LL lm[N],lz[N]; 33 struct Node{ 34 int j,k,msk; 35 Node(int _j,int _k,int _msk):j(_j),k(_k),msk(_msk){ 36 } 37 }; 38 queue<Node> que[2]; 39 40 void updata(int h,int j,int k,int msk,int val){ 41 if ( j > K || val == 0 || k < 0 ) return; 42 dp[h][j][k][msk] += val; 43 dp[h][j][k][msk] %= MOD; 44 if ( !mark[h][j][k][msk] ){ 45 mark[h][j][k][msk] = 1; 46 que[h].push( Node(j,k,msk) ); 47 } 48 } 49 50 LL quick_sqr( LL K , int g ){ 51 LL ret = 1; 52 while ( g ) { 53 if ( g&1 ) ret = ret*K%MOD; 54 K = K*K%MOD; 55 g >>= 1; 56 } 57 return ret; 58 } 59 LL C(int x,int y , bool flag = false){ 60 if ( flag ) return lm[y]*lm[x-y]%MOD*lz[x]%MOD; 61 if ( x == y ) return 1ll; 62 if ( y < 0 ) return 0ll; 63 return lm[x]*lz[y]%MOD*lz[x-y]%MOD; 64 } 65 66 int main(){ 67 lm[0] = 1; 68 for(int i = 1; i<=1000; i++) lm[i] = lm[i-1]*i%MOD; 69 for(int i = 0; i<=1000; i++) lz[i] = quick_sqr( lm[i], MOD-2 ); 70 scanf("%d%d", &n, &K ); 71 memset( dp , 0 , sizeof(dp) ); 72 dp[0][0][0][2] = 1; 73 que[0].push( Node(0,0,2) ); 74 int g = 1 , h = 0; 75 LL ans = 0; 76 Rep(i,n){ 77 swap(g,h); 78 while ( !que[g].empty() ){ 79 int j = que[g].front().j, k = que[g].front().k, msk = que[g].front().msk; 80 que[g].pop(); 81 82 if ( i == n-1 ){ 83 if ( j+((msk&2)?0:1) == K ) { ans += dp[g][j][k][msk]; ans %= MOD; } 84 } 85 else{ 86 LL tol = C(n-1-i,k), A = C(n-1-i-1,k-1); 87 LL B = (tol-A+MOD)%MOD; 88 tol = C(n-1-i,k,1); 89 LL tmp = dp[g][j][k][msk]*tol%MOD; 90 91 updata( h , j+1, k , ((msk<<1)|1)&3 , tmp*B%MOD ); 92 if ( n-1-i>k ) updata( h , j , k , ((msk<<1)|1)&3 , tmp*A%MOD*(n-1-i-k)%MOD ); 93 if ( n-2-i>k ) updata( h , j , k+1, ((msk<<1)|0)&3 , tmp*B%MOD*(n-2-i-k)%MOD ); 94 95 int last = n-1-i-k+((msk&1)?0:1)+((msk&2)?0:1); 96 97 updata( h , j , k-1 , ((msk<<1)|1)&3, tmp*A%MOD*(n-i-last)%MOD ); 98 updata( h , j , k , ((msk<<1)|0)&3, tmp*B%MOD*(n-i-last)%MOD ); 99 if ( (msk&2)==0 ) { 100 updata( h , j+1 , k , ((msk<<1)|0)&3 , tmp*B%MOD ); 101 updata( h , j+1 , k-1 , ((msk<<1)|1)&3 , tmp*A%MOD ); 102 } 103 if ( (msk&1)==0 ) { 104 updata( h , j , k , 2 , tmp*B%MOD ); 105 updata( h , j , k-1 , 3 , tmp*A%MOD ); 106 } 107 } 108 109 dp[g][j][k][msk] = 0; 110 mark[g][j][k][msk] = 0; 111 } 112 } 113 cout<<ans<<endl; 114 return 0; 115 }
第一个程序毫无压力的超时之后,开始考虑进一步优化。。。大概就是把第一个方法中k那一维状态删掉,变成dp[i][j][msk],其中i,j,msk意义与上文一致(j意义有所改变)。。显然对于这个dp求出来的不是答案。。Σdp[i][j][0..3] 将代表前i位出现j个满足要求的位置,其他(i-j)个位置可以填上任意数字!。。。请着重注意红色字样,令num[K] = Σdp[n][K][0..3] , 由于如果我们已经知道其中有K个满足条件的位置,剩下的可以任意填充,所以此时我们先让 num[K] *= (n-K)! ,由于填充的任意性所以此时满足条件的位置数量可以能变成K+i,但是我们只需要利用减法减掉满足条件位置数量改变的排列即可。现在我们考虑要减掉什么,假设填充完满足条件位置数量变成K+i,逆向考虑一个满足条件位置数为K+i的排列,可以知其被填充出来的次数为C(K+i,K),则其总变成K+i的方案数必然等于 ans[K+i]*C(K+i,K)。。综上最后公式为 ans[K] = (n-K)!*Σdp[n][K][0..3] - Σ(ans[K+i]*C(K+i,K));
非常非常优雅的降维方案,dp过程中先放松条件,最终利用数学方法再把条件重新加上!
1 //By Lin 2 #include<cstdio> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #include<set> 7 #include<vector> 8 #include<map> 9 #include<queue> 10 #include<cctype> 11 #include<cmath> 12 13 #define eps 1e-9 14 #define N 1010 15 #define sqr(x) ((x)*(x)) 16 #define Rep(i,n) for(int i = 0; i<n; i++) 17 #define foreach(i,n) for( __typeof(n.begin()) i = n.begin(); i!=n.end(); i++) 18 #define X first 19 #define Y second 20 #define mp(x,y) make_pair(x,y) 21 22 using namespace std; 23 typedef long long LL; 24 typedef pair<int,int> pii; 25 26 //int n,data[N],ans[N]; 27 #define MOD 1000000007 28 29 int n,K; 30 int dp[N][N][4]; 31 LL lm[N],lz[N]; 32 LL ans[N]; 33 34 LL quick_sqr( LL K , int g ){ 35 LL ret = 1; 36 while ( g ) { 37 if ( g&1 ) ret = ret*K%MOD; 38 K = K*K%MOD; 39 g >>= 1; 40 } 41 return ret; 42 } 43 LL C(int x,int y , bool flag = false){ 44 if ( flag ) return lm[y]*lm[x-y]%MOD*lz[x]%MOD; 45 if ( x == y ) return 1ll; 46 if ( y < 0 ) return 0ll; 47 return lm[x]*lz[y]%MOD*lz[x-y]%MOD; 48 } 49 50 int main(){ 51 lm[0] = 1; 52 for(int i = 1; i<=1000; i++) lm[i] = lm[i-1]*i%MOD; 53 for(int i = 0; i<=1000; i++) lz[i] = quick_sqr( lm[i], MOD-2 ); 54 scanf("%d%d", &n, &K ); 55 dp[0][0][2] = 1; 56 Rep(i,n) Rep(j,i+1) Rep(msk,4) 57 if ( dp[i][j][msk] ){ 58 if ( (msk&2) == 0 ) { 59 dp[i+1][j+1][(msk<<1)&3] += dp[i][j][msk]; 60 dp[i+1][j+1][(msk<<1)&3] %= MOD; 61 } 62 if ( i<n-1 ){ 63 dp[i+1][j+1][(msk<<1)&3|1] += dp[i][j][msk]; 64 dp[i+1][j+1][(msk<<1)&3|1] %= MOD; 65 } 66 dp[i+1][j][(msk<<1)&3] += dp[i][j][msk]; 67 dp[i+1][j][(msk<<1)&3] %= MOD; 68 } 69 for(int i = n; i>=K; i--){ 70 ans[i] = lm[n-i]*(dp[n][i][0]+dp[n][i][2])%MOD; 71 for(int j = i+1; j<=n; j++) 72 ans[i] = (ans[i]-ans[j]*C(j,i)%MOD)%MOD; 73 } 74 ans[K] += MOD; 75 ans[K] %= MOD; 76 cout<<ans[K]<<endl; 77 return 0; 78 }