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 }