题解:P10894 虚树

题目传送门
树形 dp 好题。

题意

给定一棵树,每次询问删去某一子树后的树能形成的虚树数量。虽然题目叫虚树,但却是和虚树没啥关系(

思路

拿到这种题,肯定先考虑树形 dp。定义 \(f_i\) 为为 \(i\) 为根的子树好的非空点集数量,考虑转移,显然在转移时会有选 \(i\) 和不选 \(i\) 两种情况。

  • 不选 \(i\),只能从若干子树中选择一棵子树中的点,方案总数为 \(\sum_{v\in son} f_v\)
  • \(i\),子树就可以随便选了,直接相乘得到 \(\prod_{v\in son} (f_v+1)\)

这样就可以进行转移了:

\[f_u=\sum_{v\in son} f_v+\prod_{v\in son} (f_v+1) \]

现在考虑删除子树,发现本质上就是求删除掉一个点后对根产生的影响。

通过观察上式,发现 \(f_i\)\(f_{fa_i}\) 的贡献是 \(f_i\times (\prod(f_{bro_i}+1)+1)\),这里 \(bro_i\)\(i\) 的兄弟节点,后面这个因式可以在转移 \(f\) 时同时维护,具体的,把 \(\prod_{v\in son} (f_v+1)\) 除掉 \(f_i+1\) 即可。

然后我们处理出一个前缀积,计为 \(g\)\(g_i\) 就表示删掉 \(i\) 对根的影响,询问时再乘上 \(f_i\) 就行了,输出 \(f_1-f_i\times g_i\)

复杂度 \(\mathcal{O(n \log n)}\)\(\log\) 是求逆元带的,注意开 long long 和一步一模防止爆炸。

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+5;
const int mod=998244353;
inline int read();
int n,m,head[N],cnt;
int f[N],g[N]; 
struct node{
	int to,next;
}edge[N<<1];
void add(int u,int v)
{
	edge[++cnt].to=v;
	edge[cnt].next=head[u];
	head[u]=cnt;
}
int ksm(int a,int b)
{
	int ans=1;
	while(b)
	{
		if(b&1) ans=a*ans%mod;
		a=a*a%mod;
		b>>=1; 
	}
	return ans;
}
void dfs1(int x,int fa)
{
	int pro=1,sum=0;
	for(int i=head[x];i;i=edge[i].next)
	{
		int to=edge[i].to;
		if(to==fa) continue;
		dfs1(to,x);
		pro=pro*(f[to]+1)%mod;
		sum=sum+f[to]%mod;
	}
	f[x]=pro+sum%mod;
	for(int i=head[x];i;i=edge[i].next)
	{
		int to=edge[i].to;
		g[to]=(pro*ksm(f[to]+1,mod-2)+1)%mod; 
	}
}
void dfs2(int x,int fa)
{
	for(int i=head[x];i;i=edge[i].next)
	{
		int to=edge[i].to;
		if(to==fa) continue;
		if(x!=1) g[to]=g[x]*g[to]%mod;
		dfs2(to,x);
	}
} 
signed main()
{
	n=read();
	for(int i=1;i<n;i++)
	{
		int x,y;
		x=read();y=read();
		add(x,y);
		add(y,x);
	}
	dfs1(1,0);
	dfs2(1,0);
	m=read();
	while(m--)
	{
		int x;
		x=read();
		printf("%lld\n",(f[1]-(f[x]*g[x]%mod)+mod)%mod);
	}
	return 0;
}

inline int read()
{
	int x=0,f=1;
	char ch;
	ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-') f=-f;ch=getchar();}
	while(ch<='9'&&ch>='0')
	{
		x=(x<<1)+(x<<3)+(ch&15);
		ch=getchar();
	}
    return x*f;
}
posted @ 2024-08-23 16:34  一只小咕咕  阅读(75)  评论(1)    收藏  举报