LOJ#2540 随机算法

题意:给定图,随机一个排列,依次加点,如果加点之后不是独立集就不加。求最后得到一个最大独立集的概率。

解:就是求有多少个排列可以加出最大独立集。

显然有一个3n的状压DP,0表示没加,1表示没加上,2表示加上了。

转移的时候枚举下一个加哪个点即可。这样有30分。

然后还是过不了的。考虑怎么压成二进制。我们可以用1表示这个点不能加(与某个加入的点相邻或者已经加入),0表示这个点可以加。

这样会损失一个信息,你就不知道当前独立集多大。所以再开一维表示独立集大小。

每次转移的时候考虑加哪一个点。顺便把它相邻的点也加上。注意它相邻的点加入的时候有顺序。具体来说,我们之前的状态中如果有x个点,那么就还有n - x个空位。而其中最前面的一个空位肯定是你主动加进去的点。所以现在要在n - x - 1个空位中放进被动加进去的点。这就是一个排列数。

然后就有了一个n2n的DP了。注意预处理出与一个点相邻的点和一个状态中点的个数。

  1 #include <cstdio>
  2 
  3 typedef long long LL;
  4 const int N = 21;
  5 const LL MO = 998244353;
  6 
  7 struct Edge {
  8     int nex, v;
  9 }edge[N * N * 2]; int top;
 10 
 11 int n, e[N], cnt[1 << 20], nb[N];
 12 LL f[N][1 << 20], nn[N], inv[N], invn[N];
 13 bool vis[N];
 14 
 15 inline void add(int x, int y) {
 16     top++;
 17     edge[top].v = y;
 18     edge[top].nex = e[x];
 19     e[x] = top;
 20     return;
 21 }
 22 
 23 inline void out(int x) {
 24     for(int i = 0; i < n; i++) {
 25         printf("%d", (x >> i) & 1);
 26     }
 27     return;
 28 }
 29 
 30 inline LL C(int n, int m) {
 31     return nn[n] * invn[m] % MO * invn[n - m] % MO;
 32 }
 33 inline LL P(int n, int m) {
 34     if(m > n) {
 35         return 0;
 36     }
 37     return nn[n] * invn[n - m] % MO;
 38 }
 39 
 40 int main() {
 41     int m;
 42     scanf("%d%d", &n, &m);
 43     for(int i = 1, x, y; i <= m; i++) {
 44         scanf("%d%d", &x, &y);
 45         add(x, y);
 46         add(y, x);
 47     }
 48     int lm = (1 << n);
 49     for(int s = 1; s < lm; s++) {
 50         cnt[s] = 1 + cnt[(s - (s & (-s))) >> 1];
 51     }
 52     for(int x = 0; x < n; x++) {
 53         nb[x] = 1 << x;
 54         for(int i = e[x + 1]; i; i = edge[i].nex) {
 55             int y = edge[i].v - 1;
 56             nb[x] |= (1 << y);
 57         }
 58     }
 59     nn[0] = inv[0] = invn[0] = 1;
 60     nn[1] = inv[1] = invn[1] = 1;
 61     for(int i = 2; i <= n; i++) {
 62         nn[i] = nn[i - 1] * i % MO;
 63         inv[i] = inv[MO % i] * (MO - MO / i) % MO;
 64         invn[i] = invn[i - 1] * inv[i] % MO;
 65     }
 66 
 67     int ans = 0;
 68     LL sum = 0;
 69     f[0][0] = vis[0] = 1;
 70     for(int i = 0; i <= n && vis[i]; i++) {
 71         for(int s = 0; s < lm; s++) {
 72             // f[i][s]
 73             if(!f[i][s]) {
 74                 continue;
 75             }
 76             //printf("f %d ", i); out(s); printf(" = %lld \n", f[i][s]);
 77             if(i > ans) {
 78                 ans = i;
 79                 sum = f[i][s];
 80             }
 81             else if(i == ans) {
 82                 sum = (sum + f[i][s]) % MO;
 83             }
 84             for(int j = 0; j < n; j++) {
 85                 if((s >> j) & 1) {
 86                     continue;
 87                 }
 88                 int t = s | nb[j];
 89                 // f[i + 1][t]
 90                 (f[i + 1][t] += f[i][s] * P(n - cnt[s] - 1, cnt[t] - cnt[s] - 1) % MO) %= MO;
 91                 vis[i + 1] = 1;
 92                 //printf("f %d ", i + 1); out(t); printf(" += f %d ", i); out(s); printf(" * %lld \n", P(n - cnt[s] - 1, cnt[t] - cnt[s] - 1));
 93             }
 94         }
 95     }
 96 
 97     printf("%lld\n", sum * invn[n] % MO);
 98     //printf("%d %lld \n", ans, sum);
 99     return 0;
100 }
AC代码

 

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

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