D. Rebuild Tree

  • 考虑\(\prod s[i]\)的组合意义:就是在每个连通块内选一个点的方案数
  • 应用链式前向星存图时,应当舍弃“0-1”变换,从2号边开始编号【对于其他情况,也应尽量避免从0开始编号】
  • 枚举子树大小DP是O(n^2)的,但如果有m的限制,可以证明时间复杂度降至O(nm)
  • 因为出点和入点未必相同,所以不能简单地把连通块大小相乘;但相信最后的结果是美的,可以大胆猜想把m+1换成n
  • 加法式枚举的好处在于,保证了两边都是合法的,避免了复杂度的错误
  • Prufer序列:有标号无根树计数,因此最后一定会剩下1个n
点击查看代码
#include <bits/stdc++.h>
using namespace std;
vector<int>a[50005];
int s[50005],n,m;
const int mod=998244353;
long long f[50005][105][2],g[105][2];
void dp(int n1)
{
	s[n1]=1;
	f[n1][0][0]=1;
	f[n1][0][1]=1;
	for(int i=0;i<a[n1].size();i++)
	{
		if(!s[a[n1][i]])
		{
			dp(a[n1][i]);
			int k=a[n1][i];
			memcpy(g,f[n1],sizeof(f[n1]));
			memset(f[n1],0,sizeof(f[n1]));
			for(int j=0;j<=min(s[n1]-1,m);j++)
			{
				for(int l=0;l<=min(s[k]-1,m);l++)
				{
					if(j+l>m)
					{
						break;
					}
					(f[n1][j+l][0]+=(g[j][0]*f[k][l][0]%mod))%=mod;
					(f[n1][j+l][1]+=(g[j][1]*f[k][l][0]%mod+g[j][0]*f[k][l][1]%mod))%=mod;
					if(j+l+1<=m)
					{
						(f[n1][j+l+1][0]+=(g[j][0]*f[k][l][1]%mod))%=mod;
						(f[n1][j+l+1][1]+=(g[j][1]*f[k][l][1]%mod))%=mod;
					}
				}
			}
			s[n1]+=s[a[n1][i]];
		}
	}
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>m;
	for(int i=1;i<n;i++)
	{
		int u,v;
		cin>>u>>v;
		a[u].push_back(v);
		a[v].push_back(u);
	}
	dp(1);
	cout<<f[1][m][1]*power(n,m-1)%mod<<endl;
	return 0;
}
posted @ 2024-10-06 08:47  D06  阅读(3)  评论(0编辑  收藏  举报