CodeForces 1010F Tree

洛谷传送门

CF 传送门

educational 的。另一道类似的题是 [ABC269Ex] Antichain

考虑令 bu=auvsonuav。那么 i=1nbi=a1=x,且 i[1,n],bi0。所以最后连通块内有 y 个点,那么贡献系数为 (x+y1y1)。所以转为计算包含 1 的连通块有 i 个点的方案数。

考虑经典树形背包,设 fu,iu 子树内包含 u 的连通块点数为 i 的方案数。特别地 fu,0=1 表示转移上去断掉这条边。记 L,R 分别为 u 的左右儿子,有:

fu,i+j+1fL,ifR,j

写成生成函数的形式,就是:

Fu(x)=xFL(x)FR(x)+1

你发现这玩意直接做优化不了,因为 Fu(x) 的次数是 szu 级别的。这启发我们想到重链剖分。

具体地,考虑在重链顶处计算重链顶的多项式 Fu(x)。设重链上的点从浅到深依次为 a1,a2,,anai 的轻儿子为 bi(为了方便若没有轻儿子则 bi=0F0(x)=1),我们有:

Fan(x)=xFbn(x)+1

Fan1(x)=xFan(x)Fbn1(x)=xFbn1(x)(xFbn(x)+1)+1

以此类推,设 Gi=xFbi(x),那么 Fu(x)=G1(G2((Gn+1))+1)+1=(i=1nj=1iGj)+1

这个东西可以分治 NTT 计算。具体就是每次递归 [l,r] 返回一个二元组 (i=lrj=liGj,i=lrGi),那么 [l,mid][mid+1,r] 的信息就可以合并了。

考虑每次计算的 Gi 次数之和为一棵树所有轻儿子的子树大小 =O(nlogn),分治 NTT 再带两个 log,总时间复杂度就是 O(nlog3n)。实际运行效率还可以。

code
// Problem: F. Tree
// Contest: Codeforces - Codeforces Round 499 (Div. 1)
// URL: https://codeforces.com/contest/1010/problem/F
// Memory Limit: 256 MB
// Time Limit: 7000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;
const int maxn = 500100;
const ll mod = 998244353, gg = 3;
inline ll qpow(ll b, ll p) {
ll res = 1;
while (p) {
if (p & 1) {
res = res * b % mod;
}
b = b * b % mod;
p >>= 1;
}
return res;
}
typedef vector<ll> poly;
int r[maxn];
inline poly NTT(poly a, int op) {
int n = (int)a.size();
for (int i = 0; i < n; ++i) {
if (i < r[i]) {
swap(a[i], a[r[i]]);
}
}
for (int k = 1; k < n; k <<= 1) {
ll wn = qpow(op == 1 ? gg : qpow(gg, mod - 2), (mod - 1) / (k << 1));
for (int i = 0; i < n; i += (k << 1)) {
ll w = 1;
for (int j = 0; j < k; ++j, w = w * wn % mod) {
ll x = a[i + j], y = w * a[i + j + k] % mod;
a[i + j] = (x + y) % mod;
a[i + j + k] = (x - y + mod) % mod;
}
}
}
if (op == -1) {
ll inv = qpow(n, mod - 2);
for (int i = 0; i < n; ++i) {
a[i] = a[i] * inv % mod;
}
}
return a;
}
inline poly operator * (poly a, poly b) {
a = NTT(a, 1);
b = NTT(b, 1);
int n = (int)a.size();
for (int i = 0; i < n; ++i) {
a[i] = a[i] * b[i] % mod;
}
a = NTT(a, -1);
return a;
}
inline poly operator + (poly a, poly b) {
int n = (int)a.size() - 1, m = (int)b.size() - 1;
poly res(max(n, m) + 1);
for (int i = 0; i <= n; ++i) {
res[i] = (res[i] + a[i]) % mod;
}
for (int i = 0; i <= m; ++i) {
res[i] = (res[i] + b[i]) % mod;
}
return res;
}
inline poly mul(poly a, poly b) {
int n = (int)a.size() - 1, m = (int)b.size() - 1, k = 0;
while ((1 << k) < n + m + 1) {
++k;
}
for (int i = 1; i < (1 << k); ++i) {
r[i] = (r[i >> 1] >> 1) | ((i & 1) << (k - 1));
}
poly A(1 << k), B(1 << k);
for (int i = 0; i <= n; ++i) {
A[i] = a[i];
}
for (int i = 0; i <= m; ++i) {
B[i] = b[i];
}
poly res = A * B;
res.resize(n + m + 1);
return res;
}
ll n, m;
vector<int> G[maxn];
int sz[maxn], son[maxn], b[maxn], top[maxn];
poly F[maxn], a[maxn];
void dfs(int u, int fa) {
sz[u] = 1;
int mx = -1;
vector<int> S;
for (int v : G[u]) {
if (v == fa) {
continue;
}
S.pb(v);
dfs(v, u);
sz[u] += sz[v];
if (sz[v] > mx) {
son[u] = v;
mx = sz[v];
}
}
for (int v : S) {
if (son[u] != v) {
b[u] = v;
}
}
}
void dfs2(int u, int tp) {
top[u] = tp;
if (!son[u]) {
return;
}
dfs2(son[u], tp);
for (int v : G[u]) {
if (!top[v]) {
dfs2(v, v);
}
}
}
pair<poly, poly> calc(int l, int r) {
if (l == r) {
return mkp(a[l], a[l]);
}
int mid = (l + r) >> 1;
auto L = calc(l, mid), R = calc(mid + 1, r);
return mkp(L.fst + mul(L.scd, R.fst), mul(L.scd, R.scd));
}
void dfs3(int u, int fa) {
for (int v : G[u]) {
if (v == fa) {
continue;
}
dfs3(v, u);
}
if (u == top[u]) {
int K = 0;
for (int v = u; v; v = son[v]) {
a[++K] = poly(1, 0);
for (ll x : F[b[v]]) {
a[K].pb(x);
}
}
auto res = calc(1, K);
F[u] = res.fst;
F[u][0] = 1;
}
}
void solve() {
scanf("%lld%lld", &n, &m);
for (int i = 1, u, v; i < n; ++i) {
scanf("%d%d", &u, &v);
G[u].pb(v);
G[v].pb(u);
}
dfs(1, -1);
dfs2(1, 1);
F[0].pb(1);
dfs3(1, -1);
ll ans = 0, fac = 1, mul = 1;
for (int i = 1; i <= n; ++i) {
ans = (ans + mul * qpow(fac, mod - 2) % mod * F[1][i]) % mod;
fac = fac * i % mod;
mul = mul * ((m + i) % mod) % mod;
}
printf("%lld\n", ans);
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}
posted @   zltzlt  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示