【2027算法进阶班】NOIP训练赛#17
B. T2-烟花(firework)
两个点缩成一个点 , 想象 \(u\) , \(v\) 背对背
,只考虑面对的那一边
\(f[u][i][j]\) 表示在以 \(u\) 为根的子树 ,前 \(i\) 个分支共选了 \(j\) 个分支,转移方程如下
\[f[u][i][j]=mod(f[u][i-1][j]+f[u][i-1][j-1]*siz[i])
\]
在 dfs 中枚举儿子时相当于枚举了 \(i\) ,倒序更新省掉一维
不考虑父亲节点 跑一边 dfs 更新出来 \(f\)
对于 \(u ,v\) 先取消 \(f_u\) 中 \(v\) 的贡献
然后 \(m^2\) 预处理 \(f_u\) 和 \(f_v\) 的融合版
最后算答案的时候加上 \(u\) 父亲方向的贡献
Code
#include<bits/stdc++.h>
#define ll long long
#define ac read()
using namespace std;
const int N=300005;
const int M=55;
const int Mod=998244353;
inline ll read() {
ll x=0,f=1;
int c=getchar();
while(!isdigit(c)) {
f=(c=='-')?-1:1;
c=getchar();
}
while(isdigit(c)) {
x=(x<<3)+(x<<1)+(c^48);
c=getchar();
}
return x*f;
}
struct node {
int nex,to;
} e[N<<1];
int head[N],tot;
inline void add(int u,int v) {
e[++tot].nex=head[u];
e[tot].to=v;
head[u]=tot;
}
int n,m;
ll f[N][M];
int U[N],V[N];
int FA[N],siz[N];
inline ll MOD(ll a) {
return (a+Mod)%Mod;
}
void dfs(int u,int fa) {
FA[u]=fa;
siz[u]=1;
f[u][0]=1;
for(int i=head[u]; i!=-1; i=e[i].nex) {
int to=e[i].to;
if(to==fa) continue;
dfs(to,u);
siz[u]+=siz[to];
for(int i=m; i>=1; --i) {
f[u][i]=MOD(f[u][i]+f[u][i-1]*siz[to]);
}
}
}
ll g1[M],g2[M];
int main() {
memset(head,-1,sizeof head);
n=ac,m=ac;
for(int i=1; i<n; ++i) {
int u=ac,v=ac;
add(u,v);
add(v,u);
U[i]=u,V[i]=v;
}
dfs(1,0);
for(int i=1; i<n; ++i) {
int u=U[i],v=V[i];
if(FA[u]==v) swap(u,v);
g1[0]=1;
g2[0]=0;
for(int j=1; j<=m; ++j) {
g1[j]=MOD(f[u][j]-g1[j-1]*siz[v]);
}
for(int j=0; j<=m; ++j) {
g2[j]=0;
for(int k=0; k<=j; ++k) {
g2[j]=MOD(g2[j]+g1[k]*f[v][j-k]);
}
}
int Fa=n-siz[u],ans=1;
for(int j=1; j<=m; ++j) {
ans=MOD(ans+g2[j]+g2[j-1]*Fa);
}
cout<<ans<<"\n";
}
return 0;
}

浙公网安备 33010602011771号