CF1151F - Sonya and Informatics
题意:有个长为n的01序列,求经过K次随机交换两个数之后这个序列非降的概率。n <= 100, k <= 1e9。
解:看到这个数据范围想到了矩阵快速幂...
先想一个暴力维护每个位置为1概率的DP,发现不独立...
然后想到PKUSC T2,单独确定每一位,但是这个东西不是排列,有相同的元素不好搞...
最后想到了thuPC摆家具,发现可以用那个套路。
发现最终每个状态的概率只跟距初态的距离有关。这里的距离是两个状态异或之后1的个数。
于是进行DP,设fij表示前i次操作,所得状态与初态异或后有j个1的概率。
转移有三种,分别是无用转移,挪一个1到初态的0中,挪一个1到初态的1中。
然后矩阵快速幂一下。
最后算答案的时候,我们还要算出有多少种状态满足条件,这就是一个组合数。然后除以这个组合数就行了。
1 #include <bits/stdc++.h> 2 3 const int N = 210, MO = 1e9 + 7; 4 5 int n, K, A[N], fac[N], inv[N], invn[N], a[N][N], ans[N][N], c[N][N], lm; 6 7 inline void Add(int &a, const int &b) { 8 a += b; 9 if(a >= MO) a -= MO; 10 else if(a < 0) a += MO; 11 return; 12 } 13 14 inline int qpow(int a, int b) { 15 int ans = 1; 16 while(b) { 17 if(b & 1) { 18 ans = 1ll * ans * a % MO; 19 } 20 a = 1ll * a * a % MO; 21 b = b >> 1; 22 } 23 return ans; 24 } 25 26 inline int Inv(int x) { 27 return qpow(x, MO - 2); 28 } 29 30 inline int iC(int n, int m) { 31 if(n < 0 || m < 0 || n < m) return 0; 32 return 1ll * invn[n] * fac[m] % MO * fac[n - m] % MO; 33 } 34 35 inline void mulself() { 36 memset(c, 0, sizeof(c)); 37 for(int k = 0; k <= lm; k++) { 38 for(int i = 0; i <= lm; i++) { 39 if(!a[i][k]) continue; 40 for(int j = 0; j <= lm; j++) { 41 Add(c[i][j], 1ll * a[i][k] * a[k][j] % MO); 42 } 43 } 44 } 45 memcpy(a, c, sizeof(a)); 46 return; 47 } 48 49 inline void mul() { 50 memset(c, 0, sizeof(c)); 51 for(int k = 0; k <= lm; k++) { 52 for(int i = 0; i <= lm; i++) { 53 if(!a[i][k]) continue; 54 for(int j = 0; j <= lm; j++) { 55 Add(c[i][j], 1ll * a[i][k] * ans[k][j] % MO); 56 } 57 } 58 } 59 memcpy(ans, c, sizeof(c)); 60 return; 61 } 62 63 int main() { 64 scanf("%d%d", &n, &K); 65 int cnt = 0, aim = 0; 66 for(int i = 1; i <= n; i++) { 67 scanf("%d", &A[i]); 68 cnt += A[i]; 69 } 70 for(int i = 1; i + cnt <= n; i++) { 71 aim += A[i]; 72 } 73 //int P = 1ll * Inv(n * (n - 1) / 2) * cnt % MO * (n - cnt) % MO; 74 int P = Inv(n * (n - 1) / 2); 75 fac[0] = inv[0] = invn[0] = 1; 76 fac[1] = inv[1] = invn[1] = 1; 77 for(int i = 2; i <= n; i++) { 78 fac[i] = 1ll * i * fac[i - 1] % MO; 79 inv[i] = 1ll * inv[MO % i] * (MO - MO / i) % MO; 80 invn[i] = 1ll * invn[i - 1] * inv[i] % MO; 81 } 82 83 lm = n - cnt; 84 for(int j = 0; j <= n - cnt; j++) { 85 int t1 = 1ll * (cnt - j) * (n - cnt - j) % MO * P % MO; 86 int t2 = 1ll * j * j * P % MO; 87 Add(a[j][j], (1 - t1 + MO - t2 + MO) % MO); 88 Add(a[j][j + 1], t1); 89 Add(a[j][j - 1], t2); 90 } 91 for(int i = 0; i <= lm; i++) { 92 ans[i][i] = 1; 93 } 94 95 while(K) { 96 if(K & 1) { 97 mul(); 98 } 99 mulself(); 100 K >>= 1; 101 } 102 103 104 /*f[0][0] = 1; 105 for(int i = 0; i < K; i++) { 106 for(int j = 0; j <= n - cnt; j++) { 107 //Add(f[i + 1][j], (1ll - P + MO) * f[i][j] % MO); 108 int t1 = 1ll * (cnt - j) * (n - cnt - j) % MO * P % MO; 109 int t2 = 1ll * j * j * P % MO; 110 Add(f[i + 1][j + 1], 1ll * f[i][j] * t1 % MO); 111 Add(f[i + 1][j - 1], 1ll * f[i][j] * t2 % MO); 112 Add(f[i + 1][j], 1ll * f[i][j] * ((1 - t1 + MO - t2 + MO) % MO) % MO); 113 } 114 }*/ 115 116 int Ans = 1ll * ans[0][aim] * iC(n - cnt, aim) % MO * iC(cnt, aim) % MO; 117 printf("%d\n", (Ans + MO) % MO); 118 return 0; 119 }
我的理解方式是,有两个集合S1,S2。S1里面是初态中为1的位置,S2是初态中为0的位置。我们要往S2里面放若干个1。每次可以不改变S2中1的个数,或者从S1拿一个1过来,或者从S2拿一个1出去。
代码写的时候没注意j = 0的边界,可能会数组越界,但是A了...