CF1151F - Sonya and Informatics

1151F - 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 }
AC代码

我的理解方式是,有两个集合S1,S2。S1里面是初态中为1的位置,S2是初态中为0的位置。我们要往S2里面放若干个1。每次可以不改变S2中1的个数,或者从S1拿一个1过来,或者从S2拿一个1出去。

代码写的时候没注意j = 0的边界,可能会数组越界,但是A了...

posted @ 2019-06-02 12:22  huyufeifei  阅读(230)  评论(0编辑  收藏  举报
试着放一个广告栏(虽然没有一分钱广告费)

ReadEra 阅读书籍

『Flyable Heart 応援中!』 HHG 高苗京铃 闪十PSS 双六 電動伝奇堂 章鱼罐头制作组 はきか 祝姬 星降夜