Loj 3235 Przedszkole

容斥原理 + 子集卷积 + 拉格朗日插值 + 色多项式.

对三种子任务分别设计算法.

$m\le 24$

考虑容斥,暴力枚举哪些边连接的两个点颜色是相同的,用并查集维护相同颜色的点形成的连通块.

若钦定了 $p​$ 条边连接的两个点颜色相同,形成了 $s​$ 个连通块,贡献应为 $(-1)^p k^s​$ .

由于每加一条边最多减少 $1​$ 个连通块,所以 $s​$ 与 $n​$ 的差不会超过 $m​$ ,维护出这个关于 $k​$ 的多项式各项系数即可.

时间复杂度 $O(2^mm+qm)$ ,但可以剪枝,若搜到某条边时,两个端点同色,选它和不选它的贡献会恰好抵消掉.

$n\le 15$

从 $m\le 24$ 的做法可以注意到答案是关于 $k$ 的 $n$ 次多项式.

于是只需要代 $k=1,2,3\dots,n+1$ 求出答案,就可以插值得出这个多项式.

考虑对于一个 $k$ 如何求出答案.

染色可以看成依次为每种颜色确定点集,每种颜色的点集必须是独立集,不同颜色的点集不能相交.

记集合幂级数 $F(x)=\sum_{S\in I} x^S$ ,其中 $I$ 表示所有独立集的集合,则 $F$ 子集卷积意义下 $k$ 次幂全集的系数即为所求.

时间复杂度 $O(2^nn^3+qn)$ ,为了实现方便也可以写成 $O(2^nn^3+qn^2)$ 之类的,插值不是复杂度瓶颈.

每个点度数为 $2$

此时的图一定是由若干个互不相交的环构成的,考虑一个长度为 $l$ 的环,它的色多项式为 $(k-1)^l+(-1)^l(k-1)$ .

注意长度相同的环是没有本质差别的,可以合并起来一起算.

环长种类数是 $O(\sqrt n)$ 的,时间复杂度 $O(q\sqrt n\log n)$ .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
//%std
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int read()
{
int out = 0, fh = 1;
char jp = getchar();
while ((jp > '9' || jp < '0') && jp != '-')
jp = getchar();
if (jp == '-')
fh = -1, jp = getchar();
while (jp >= '0' && jp <= '9')
out = out * 10 + jp - '0', jp = getchar();
return out * fh;
}
void print(int x)
{
if (x >= 10)
print(x / 10);
putchar('0' + x % 10);
}
void write(int x, char c)
{
if (x < 0)
putchar('-'), x = -x;
print(x);
putchar(c);
}
const int P = 1e9 + 7;
int add(int a, int b)
{
return a + b >= P ? a + b - P : a + b;
}
void inc(int &a, int b)
{
a = add(a, b);
}
int mul(int a, int b)
{
return 1LL * a * b % P;
}
int fpow(int a, int b)
{
int res = 1;
while (b)
{
if (b & 1)
res = mul(res, a);
a = mul(a, a);
b >>= 1;
}
return res;
}
int n, m, q;
namespace sub1
{
const int N = 1e5 + 10, M = 25;
int fa[N], siz[N];
int st[M], ed[M], coef[M];
int Find(int x)
{
return x == fa[x] ? x : Find(fa[x]);
}
void dfs(int x, int p, int s)
{
if (x == m)
{
inc(coef[s], p);
return;
}
int u = Find(st[x]), v = Find(ed[x]);
if (u != v)
{
if (siz[u] > siz[v])
swap(u, v);
dfs(x + 1, p, s);
fa[u] = v, siz[v] += siz[u];
dfs(x + 1, p == 1 ? P - 1 : 1, s + 1);
fa[u] = u, siz[v] -= siz[u];
}
}
void solve()
{
iota(fa + 1, fa + 1 + n, 1);
fill(siz + 1, siz + 1 + n, 1);
for (int i = 0; i < m; ++i)
st[i] = read(), ed[i] = read();
dfs(0, 1, 0);
while (q--)
{
int k = read(), ans = 0;
int mi = max(1, n - m), pw = fpow(k, mi);
for (int i = mi; i <= n; ++i)
{
inc(ans, mul(pw, coef[n - i]));
pw = mul(pw, k);
}
write(ans, '\n');
}
}
}
namespace sub2
{
void FWT(int *a)
{
for (int l = 2; l <= (1 << n); l <<= 1)
{
int m = l >> 1;
for (int *p = a; p != a + (1 << n); p += l)
for (int i = 0; i < m; ++i)
inc(p[i + m], p[i]);
}
}
void IFWT(int *a)
{
for (int l = 2; l <= (1 << n); l <<= 1)
{
int m = l >> 1;
for (int *p = a; p != a + (1 << n); p += l)
for (int i = 0; i < m; ++i)
inc(p[i + m], P - p[i]);
}
}
const int N = 15;
int nxt[N], popc[1 << N], y[N + 1];
int a[N + 1][1 << N], res[N + 1][1 << N], tmp[N + 1][1 << N];
void solve()
{
for (int i = 0; i < m; ++i)
{
int u = read() - 1, v = read() - 1;
nxt[u] |= 1 << v, nxt[v] |= 1 << u;
}
for (int S = 0; S < (1 << n); ++S)
{
popc[S] = popc[S >> 1] + (S & 1);
bool f = true;
for (int i = 0; i < n && f; ++i)
if ((S >> i & 1) && (S & nxt[i]))
f = false;
if (f)
a[popc[S]][S] = 1;
}
res[0][0] = 1;
for (int i = 0; i <= n; ++i)
FWT(a[i]), FWT(res[i]);
int mx = (1 << n) - 1;
y[0] = 0;
for (int k = 1; k <= n; ++k)
{
for (int i = 0; i <= n; ++i)
for (int S = 0; S < (1 << n); ++S)
tmp[i][S] = 0;
for (int i = 0; i <= n; ++i)
for (int S = 0; S < (1 << n); ++S) if (a[i][S])
for (int j = 0; i + j <= n; ++j)
inc(tmp[i + j][S], mul(a[i][S], res[j][S]));
for (int i = 0; i <= n; ++i)
for (int S = 0; S < (1 << n); ++S)
res[i][S] = tmp[i][S];
IFWT(tmp[popc[mx]]);
y[k] = tmp[popc[mx]][mx];
}
while (q--)
{
int k = read();
if (k <= n)
{
write(y[k], '\n');
continue;
}
int ans = 0;
for (int i = 0; i <= n; ++i)
{
int t = y[i];
for (int j = 0; j <= n; ++j)
if (i != j)
{
t = mul(t, add(k, P - j));
t = mul(t, fpow(add(i, P - j), P - 2));
}
inc(ans, t);
}
write(ans, '\n');
}
}
}
namespace sub3
{
const int N = 1e5 + 10;
int vis[N], tot = 0, siz[N];
int a[N], b[N], c = 0;
vector<int> G[N];
int dfs(int x)
{
vis[x] = 1;
int s = 1;
for (int y : G[x])
if (!vis[y])
s += dfs(y);
return s;
}
void solve()
{
for (int i = 1; i <= n; ++i)
{
int u = read(), v = read();
G[u].push_back(v), G[v].push_back(u);
}
for (int i = 1; i <= n; ++i)
if (!vis[i])
siz[++tot] = dfs(i);
sort(siz + 1, siz + 1 + tot);
for (int l = 1, r = 1; l <= tot; l = r)
{
a[++c] = siz[l];
while (r <= n && siz[r] == siz[l])
++b[c], ++r;
}
while (q--)
{
int k = read(), ans = 1;
for (int i = 1; i <= c; ++i)
{
int t = fpow(k - 1, a[i]);
if (a[i] & 1)
inc(t, P + 1 - k);
else
inc(t, k - 1);
ans = mul(ans, fpow(t, b[i]));
}
write(ans, '\n');
}
}
}
int main()
{
n = read(), m = read(), q = read();
if (m <= 24)
sub1::solve();
else if (n <= 15)
sub2::solve();
else
sub3::solve();
return 0;
}