题目大意 【gdoi2018 day2】第二题 滑稽子图(subgraph)
-
给你一颗树(T),以及一个常数(K),对于(T)的点集(V)的子集(S).
-
定义(f(S))为点集(S)的导出子图的边数(一条原树中的边只有两个端点都出现在(S)中,才会出现在导出子图中)
数据范围
解题方案
(Part_1) 5%
- 随便做
(Part_2) 30%
-
考虑一下DP.
-
设(f[i][j][0/1])表示第(i)个点,导出子图边数为(j),第(i)个点是否选.
-
转移讨论一下即可,这里需要注意一下转移时用顺推还是逆推.
-
一般来说,对于背包类型的,我们顺推可以使每一个转移都是有效的.
-
枚举之前子树大小和,以及当前子树大小,乘积的和是(O(n^2))级别的.
-
证明:
- 两个点会产生贡献,当且仅当它们的(LCA)为当前的根时,每两个点的(LCA)唯一,故只有(O(n^2))个点会产生贡献.
(Part_3) 100% DP
-
依然是DP,但我们要利用好(kle 10)这个特性.
-
我们来观察一下顺推时的转移,其实可以表示为:
- 令当前计算的是(y)对于父亲(x)的贡献,表达如下:
[f[x][i + j] = sum_{i = 0}^{size_x - 1}sum_{j=0}^{size_y-1} f[x][i] * f[y][j] * (i + j) ^ k ]
(qquad qquad qquad qquad qquad qquad qquad qquad qquad Downarrow)
$$f[x][i + j] = sum_{i = 0}^{size_x - 1}sum_{j = 0}^{size_y - 1} f[x][i]f[y][j] * sum_{t = 0}kitj^{k-t}inom{k}{t}$$
(qquad qquad qquad qquad qquad qquad qquad qquad qquad Downarrow)
$$f[x][i + j] = sum_{i = 0}^{size_x - 1}sum_{j = 0}^{size_y - 1} sum_{t = 0}^kinom{k}{t}f[x][i]f[y][j] it*j{k-t}$$
(qquad qquad qquad qquad qquad qquad qquad qquad qquad Downarrow)
$$f[x][i + j] = sum_{i = 0}^{size_x - 1}sum_{j = 0}^{size_y - 1} sum_{t = 0}kinom{k}{t}(f[x][i]*it) (f[y][j]j^{k-t})$$
(qquad qquad qquad qquad qquad qquad qquad qquad qquad Downarrow)
$$f[x][i + j] = sum_{t = 0}^kinom{k}{t}sum_{i = 0}^{size_x - 1}sum_{j = 0}^{size_y - 1} (f[x][i]i^t) (f[y][j]j^{k-t})$$
(qquad qquad qquad qquad qquad qquad qquad qquad qquad Downarrow)
$$f[x][i + j] = sum_{t = 0}^kinom{k}{t}sum_{i = 0}^{size_x - 1} (f[x][i]i^t)sum_{j = 0}^{size_y - 1} (f[y][j]j^{k-t})$$
(qquad qquad qquad qquad qquad qquad qquad qquad qquad Downarrow)
$$令g[x][t]表示sum_{i = 0}{size_x-1}f[x][i]*it,则上式转化为$$
(qquad qquad qquad qquad qquad qquad qquad qquad qquad Downarrow)
$$f[x][i + j] = sum_{t = 0}^kinom{k}{t}g[x][t]*g[y][k-t]$$
-
我们发现实质上(f)的转移只与(g)有关,所以我们直接枚举指数(k)大小,(O(k))转移即可.
-
上述讨论的是当(x,y)两点不都选的时候,如果都选,转移应当如下:
(qquad qquad qquad qquad qquad qquad qquad qquad qquad Downarrow)
(qquad qquad qquad qquad qquad qquad qquad qquad qquad Downarrow)
(qquad qquad qquad qquad qquad qquad qquad qquad qquad Downarrow)
(qquad qquad qquad qquad qquad qquad qquad qquad qquad Downarrow)
(qquad qquad qquad qquad qquad qquad qquad qquad qquad Downarrow)
(qquad qquad qquad qquad qquad qquad qquad qquad qquad Downarrow)
(qquad qquad qquad qquad qquad qquad qquad qquad qquad Downarrow)
(qquad qquad qquad qquad qquad qquad qquad qquad qquad Downarrow)
(qquad qquad qquad qquad qquad qquad qquad qquad qquad Downarrow)
-
可以发现,最后的转移,依然是只与(g)有关.
-
转移一个点是(O(k^2))的,(n)个点,故时间复杂度为(O(n * k^3/6)),实际上可以优化到(O(n*k^2))
-
注意,我们如果有(a_1<a_2<cdots a_n 且 (1le a_i le m)),时间复杂度是(O(frac{n^m}{n!}))
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#define ll long long
#define I register int
#define L register ll
#define F(i, a, b) for (L i = a; i <= b; i ++)
#define mec(a, b) memcpy(a, b, sizeof a)
#define mem(a, b) memset(a, b, sizeof a)
#define add(a, b) ((a) = (a + b) % mo)
#define N 100100
#define M 2 * N
#define Get getchar()
#define mo 998244353
using namespace std;
ll n, m, K, u, v, ans, sum, h, x, k;
ll F[N][11][2], G[N][11][2], jc[N], ny[N], d[N], bz[N];
ll nex[M], tov[M], las[N], last[N], tot;
void R(L &x) {
char c = Get; x = 0; L t = 1;
for (; !isdigit(c); c = Get) t = (c == '-' ? -1 : t);
for (; isdigit(c); x = (x << 3) + (x << 1) + c - '0', c = Get); x = x * t;
}
void W(L x) {
if (x < 0) { putchar('-'); W(-x); return; }
if (x > 9) W(x / 10); putchar(x % 10 + '0');
}
void ins(L x, L y) { tov[++ tot] = y, nex[tot] = las[x], las[x] = tot; }
ll ksm(L x, L y) {
L ans = 1;
while (y) {
if (y & 1) ans = (ans * x) % mo;
x = (x * x) % mo, y >>= 1;
}
return ans;
}
ll C(L x, L y) { return jc[x] * ny[y] % mo * ny[x - y] % mo; }
void Dg_dp() {
bz[1] = 1, d[h = 1] = 1;
while (h) { x = d[h], k = las[x];
if (k) {
if (!bz[tov[k]]) bz[tov[k]] = 1, d[++ h] = tov[k];
las[x] = nex[k];
}
if (!k) {
G[x][0][0] = G[x][0][1] = 1;
for (L k = last[x], y, s; k; k = nex[k])
if ((y = tov[k]) && (!bz[y])) {
mem(F[x], 0);
F(j, 0, K)
F(k, 0, j) {
add(F[x][j][0], G[x][k][0] * (G[y][j - k][0] + G[y][j - k][1]) % mo * C(j, k));
add(F[x][j][1], G[x][k][1] * (G[y][j - k][0]) % mo * C(j, k)); s = 0;
F(p, 0, k) add(s, G[x][p][1] * G[y][k - p][1] % mo * C(k, p));
add(F[x][j][1], C(j, k) * s);
}
F(j, 0, K) G[x][j][0] = F[x][j][0], G[x][j][1] = F[x][j][1];
}
bz[d[h --]] = 0;
}
}
}
int main() {
freopen("subgraph.in", "r", stdin);
freopen("subgraph.out", "w", stdout);
R(n), R(m), R(K), jc[0] = ny[0] = 1;
F(i, 1, m) R(u), R(v), ins(u, v), ins(v, u);
F(i, 1, K) jc[i] = (jc[i - 1] * i) % mo, ny[i] = ksm(jc[i], mo - 2);
if (K == 0) { printf("%d", ksm(2, n)); return 0;}
mec(last, las), Dg_dp(), W((G[1][K][0] + G[1][K][1]) % mo);
}