Rebuild Tree

D06發表於2024-10-06
  • 考慮\(\prod s[i]\)的組合意義:就是在每個連通塊內選一個點的方案數
  • 應用鏈式前向星存圖時,應當捨棄“0-1”變換,從2號邊開始編號【對於其他情況,也應儘量避免從0開始編號】
  • 列舉子樹大小DP是O(n^2)的,但如果有m的限制,可以證明時間複雜度降至O(nm)
  • 因為出點和入點未必相同,所以不能簡單地把連通塊大小相乘;但相信最後的結果是美的,可以大膽猜想把m+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];
int power(int n,int p)
{
	if(p==0)
	{
		return 1;
	}
	long long tmp=power(n,p/2);
	if(p%2==1)
	{
		return tmp*tmp%mod*n%mod;
	}
	return tmp*tmp%mod;
}
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;
}

相關文章