BZOJ4671 异或图

Posted cot

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ4671 异或图相关的知识,希望对你有一定的参考价值。

Description

定义两个结点数相同的图 G1 与图 G2 的异或为一个新的图 G, 其中如果 (u, v) 在 G1 与
G2 中的出现次数之和为 1, 那么边 (u, v) 在 G 中, 否则这条边不在 G 中.
现在给定 s 个结点数相同的图 G1...s, 设 S = {G1, G2, . . . , Gs}, 请问 S 有多少个子集的异
或为一个连通图?

Input

第一行为一个整数s, 表图的个数.
接下来每一个二进制串, 第 i 行的二进制串为 gi, 其中 gi 是原图通过以下伪代码转化得
到的. 图的结点从 1 开始编号, 下面设结点数为 n.
Algorithm 1 Print a graph G = (V, E)
for i = 1 to n do
for j = i + 1 to n do
if G contains edge (i, j) then
print 1
else
print 0
end if
end for
end for
 2 ≤ n ≤ 10,1 ≤ s ≤ 60.

Output

输出一行一个整数, 表示方案数

Sample Input

3
1
1
0

Sample Output

4

HINT

Solution

这道题的出处是去年我们省的省队集训。

回想起去年省队集训的时候,非正式选手的我看到这道题和这道题题解时的一脸懵逼。

“为什么是2^全零行个数次方啊?”

“斯特林数是啥子啊?”

“贝尔数又是啥子啊?”

“这题为什么我看了题解还是不知道怎么做啊?”

“为什么标程的代码这么短啊?”

“……”

时隔了整整一年,重新拿到这道题,感慨颇多。

首先,连通的条件并不好算,我们考虑不连通的情况。

我们先枚举一个划分,表示不同的划分里的点一定在不同的连通块,但在同一个划分里的点不一定在同一个连通块。也就是说所有连接两个不同划分的边都必须为0。这个的方案数可以用高斯消元算,答案会等于2^s-基的个数。

然后我们再考虑每种方案都被重复算了多少遍。一个划分集合如果是另一个划分集合的子集的话那么它被重复算的系数是斯特林数,我们可以暴力把这个容斥系数算出来(如果打表打出来会发现其实是阶乘)。

复杂度是贝尔数的第N项*高斯消元的。

代码的确很短。

Code

 1 #include <cstdio>
 2 #include <bitset>
 3 #include <cstring>
 4 #define R register
 5 typedef long long ll;
 6 char str[110];
 7 int s, n, id[110][110], len;
 8 std::bitset<110> b[66], t, c[66];
 9 int col[11];
10 ll pw[66], ans, f[110], S[110][110];
11 void dfs(R int x, R int cl)
12 {
13     if (x > n)
14     {
15         R int cnt = 0, ji = 0;
16         for (R int i = 1; i <= n; ++i)
17             for (R int j = i + 1; j <= n; ++j)
18                 if (col[i] != col[j]) t.set(cnt), ++cnt;
19                 else t.reset(cnt), ++cnt;
20         for (R int i = 1; i <= s; ++i)
21         {
22             c[i] = b[i] & t;
23 //            for (R int j = 0; j < len; ++j) printf("%d", c[i][j] == 1); puts("");
24         }
25         for (R int i = 1, bs = 0; i <= s && bs < len; )
26         {
27             if (c[i][bs] == 0)
28             {
29                 for (R int j = i + 1; j <= s; ++j)
30                     if (c[j][bs])
31                     {
32                         std::swap(c[i], c[j]);
33                         break;
34                     }
35             }
36             if (c[i][bs] == 0) {++bs; continue;}
37             ++ji;
38             for (R int j = i + 1; j <= s; ++j)
39                 if (c[j][bs])
40                     c[j] ^= c[i];
41             ++i; ++bs;
42         }
43 //        for (R int i = 1; i <= n; ++i) printf("%d ", col[i]); puts("");
44 //        printf("base %d pw %lld\n", ji, f[cl] * pw[s - ji]);
45         ans += f[cl] * pw[s - ji];
46         return ;
47     }
48     for (R int i = 1; i <= cl; ++i)
49     {
50         col[x] = i;
51         dfs(x + 1, cl);
52     }
53     col[x] = cl + 1;
54     dfs(x + 1, cl + 1);
55 }
56 int main()
57 {
58     scanf("%d", &s);
59     pw[0] = 1;
60     for (R int i = 1; i <= s; ++i)
61     {
62         scanf("%s", str); pw[i] = pw[i - 1] * 2;
63         len = strlen(str);
64         for (n = 2; n <= 10; ++n) if (n * (n - 1) == len * 2) break;
65         for (R int j = 0; j < len; ++j) b[i][j] = (str[j] == 1);
66     }
67 //    for (R int i = 1; i <= s; ++i) {for (R int j = 0; j < len; ++j) printf("%d", b[i][j] == 1); puts("");}
68     S[1][1] = 1;
69     for (R int i = 2; i <= n; ++i)
70     {
71         S[i][1] = 1;
72         for (R int j = 2; j <= i; ++j)
73             S[i][j] = S[i - 1][j - 1] + j * S[i - 1][j];
74     }
75     f[1] = 1;
76     for (R int i = 2; i <= n; ++i)
77     {
78         for (R int j = i - 1; j; --j)
79             f[i] -= S[i][j] * f[j];
80 //        printf("%lld\n", f[i]);
81     }
82     R int cnt = 0;
83     for (R int i = 1; i <= n; ++i)
84         for (R int j = i + 1; j <= n; ++j)
85             id[i][j] = id[j][i] = cnt++;
86     dfs(1, 0);
87     printf("%lld\n", ans);
88     return 0;
89 }
90 /*
91 3
92 111
93 111
94 111
95 */

 

以上是关于BZOJ4671 异或图的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 4671: 异或图

bzoj4671: 异或图

BZOJ4671 异或图

bzoj 4671 异或图

bzoj4671: 异或图

bzoj 4671 异或图 —— 容斥+斯特林反演+线性基