【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)空间复杂度。。

View Code
  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过程中先放松条件,最终利用数学方法再把条件重新加上!

View Code
 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 }

 

posted @ 2013-03-23 00:02  lzqxh  阅读(227)  评论(0编辑  收藏  举报