Loading

『MdOI R2』Resurrection

链接:https://www.luogu.com.cn/problem/P6383

题目描述:给定一棵树(大根堆),每次可以删除一条边\((u,v)\),然后将\(u\)所在连通块的最大元素与\(v\)所在连通块的最大元素连边,求能构造出多少个本质不同的树。

题解:可以发现构造出来的树也是一棵树(大根堆),所以每个点可以与所有自己的祖先节点连边,然后我们还可以发现构造出来的树合法当且仅当所有边在原数上不交。

这样原问题就转化为了在树上构造一个每个点可以与所有自己的祖先节点连边的树并使得所有树上路径在原树上不交(包含关系除外)。

所以我们可以令\(dp_{i,j}\)表示第\(i\)个节点,祖先中还有\(j\)个可以匹配的方案数。

\(i!=n\)时,我们需要将一个祖先节点用掉,但这个祖先节点仍可被叶子节点用到,则对于其子树祖先节点个数,范围缩到了[\(2\)(将\(i\)连向\(n\)则只有\(i\)\(n\)可用,\(j+1\)(加一个\(i\)节点,恰好连父亲节点而无贡献)]

\(i==n\)时,范围变为了[\(1\),\(j+1\)],因为\(n\)节点不要连边。

然后\(dp\)转移即可。

#include<iostream>
#include<cstdio>
#define mod 998244353
using namespace std;
struct node
{
	int v,nxt;
};
node edge[8001];
long long n,len,dp[4001][4001],s[4001][4001],head[4001];
void add(int x,int y)
{
	edge[++len].v=y;
	edge[len].nxt=head[x];
	head[x]=len;
	return;
}
bool used[4001];
void dfs(int x)
{
	used[x]=1;
	for (int i=0;i<=n;++i)
		s[x][i]=1;
	for (int i=head[x];i>0;i=edge[i].nxt)
		if (!used[edge[i].v])
		{
			dfs(edge[i].v);
			for (int j=0;j<=n;++j)
				s[x][j]=s[x][j]*dp[edge[i].v][j]%mod;
		}
	for (int i=1;i<=n;++i)
		s[x][i]=(s[x][i]+s[x][i-1])%mod;
	if (x!=n)
	{
		for (int i=1;i<=n;++i)
			dp[x][i]=s[x][i+1]-s[x][1];
	}
	else
	{
		for (int i=0;i<=n;++i)
			dp[x][i]=s[x][i+1]-s[x][0];
	}
	return;
}
int read()
{
	char c=0;
	int sum=0;
	while (c<'0'||c>'9')
		c=getchar();
	while ('0'<=c&&c<='9')
	{
		sum=sum*10+c-'0';
		c=getchar();
	}
	return sum;
}
int main()
{
	long long x,y,mul=1;
	n=read();
	for (int i=1;i<=n-1;++i)
	{
		cin>>x>>y;
		add(x,y);
		add(y,x);
	}
	dfs(n);
	printf("%lld\n",(dp[n][0]%mod+mod)%mod);
	return 0;
}
posted @ 2022-12-14 21:46  zhouhuanyi  阅读(42)  评论(0)    收藏  举报