1 条题解
-
1
这题的题意就是考虑一棵点分树的还原。考虑任意一棵子树接上它根结点的父亲。可以发现,这棵子树的任何一个结点都可以接上 ,因为点分树完全改变了原树结构, 的儿子只是它对应子树的重心,与实际位置没有任何关系。而路径一共有两种情况:一端是的路径和两端都不是 ,即原子树内部的路径。原子树内部的直接上传答案就好了,连接 的贡献也比较好计算; 但要注意一条长度不小于 的路径有两种连接 的方式。
接下来考虑 的所有子树合并的情况。子树合并时需要考虑跨子树的路径,即两个子树内连接的那些路径的结合。分别枚举长度,利用之前情况得到的信息,边dfs边依次计算贡献即可。
同时需要注意的是,每条路径都要乘上与它无关的部分的方案数,这部分意味着除了它之外其他结点对原树可能的贡献。在合并为新路径前就需要计算好,因为它在所有的原树里都有贡献。这部分的贡献也可以边dfs边计算,想清楚各个dp的顺序即可。
最后输出每一个对应长度的方案数即可。复杂度瓶颈在跨子树的方案计算,由于点分树每层结点数至少扩大2倍,考虑最长链长度,时间复杂度可能是。
实现其实很短。提供一份jiangly的solution:
#include <cstdio> #include<algorithm> #include<vector> #include<iostream> #include<cassert> constexpr int P = 998244353; using i64 = long long; // assume -P <= x < 2P int norm(int x) { if (x < 0) { x += P; } if (x >= P) { x -= P; } return x; } template<class T> T power(T a, int b) { T res = 1; for (; b; b /= 2, a *= a) { if (b % 2) { res *= a; } } return res; } struct Z { int x; Z(int x = 0) : x(norm(x)) {} int val() const { return x; } Z operator-() const { return Z(norm(P - x)); } Z inv() const { assert(x != 0); return power(*this, P - 2); } Z &operator*=(const Z &rhs) { x = i64(x) * rhs.x % P; return *this; } Z &operator+=(const Z &rhs) { x = norm(x + rhs.x); return *this; } Z &operator-=(const Z &rhs) { x = norm(x - rhs.x); return *this; } Z &operator/=(const Z &rhs) { return *this *= rhs.inv(); } friend Z operator*(const Z &lhs, const Z &rhs) { Z res = lhs; res *= rhs; return res; } friend Z operator+(const Z &lhs, const Z &rhs) { Z res = lhs; res += rhs; return res; } friend Z operator-(const Z &lhs, const Z &rhs) { Z res = lhs; res -= rhs; return res; } friend Z operator/(const Z &lhs, const Z &rhs) { Z res = lhs; res /= rhs; return res; } }; int main() { char INPUT[50],OUTPUT[50]; /* for(int TEST=0;TEST<=33;++TEST) { std::cerr<<TEST<<std::endl; sprintf(INPUT,"MSF_Akatsuki%d.in",TEST); sprintf(OUTPUT,"MSF_Akatsuki%d.out",TEST); freopen(INPUT,"r",stdin); freopen(OUTPUT,"w",stdout);*/ int n, rt; std::cin >> n >> rt; rt--; std::vector<std::vector<int>> adj(n); for (int i = 0; i < n - 1; i++) { int u, v; std::cin >> u >> v; u--; v--; adj[u].push_back(v); adj[v].push_back(u); } std::vector<int> siz(n); std::vector f(n, std::vector<Z>(n)); std::vector<Z> w(n); auto dfs = [&](auto self, int u, int p) -> void { siz[u] = 1; w[u] = 1; f[u][0] = 1; std::vector<Z> g(n); g[0] = 1; for (auto v : adj[u]) { if (v == p) { continue; } self(self, v, u); std::vector<Z> nf(n), ng(n); for (int i = 0; i < siz[u]; i++) { nf[i] += f[u][i] * w[v] * siz[v]; ng[i] += g[i] * w[v] * siz[v]; } for (int i = 0; i < siz[v]; i++) { nf[i] += f[v][i] * w[u] * siz[v]; ng[i + 1] += f[v][i] * w[u] * (1 + (i > 0)); } for (int i = 0; i < siz[v]; i++) { for (int j = 0; j < siz[u]; j++) { nf[i + j + 1] += f[v][i] * (1 + (i > 0)) * g[j]; } } f[u] = nf; g = ng; w[u] *= w[v] * siz[v]; siz[u] += siz[v]; } for (auto v : adj[u]) { if (v == p) { continue; } if (2 * siz[v] > siz[u]) { for (int i = 1; i < n; i++) { std::cout << 0 << " \n"[i == n - 1]; } std::exit(0); } } }; dfs(dfs, rt, -1); for (int i = 1; i < n; i++) { std::cout << f[rt][i].val() << " \n"[i == n - 1]; } /* fclose(stdin); fclose(stdout); }*/ return 0; }
信息
- ID
- 157
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- (无)
- 递交数
- 78
- 已通过
- 2
- 上传者