F. Regular Forestatio
题意:定义一个好的点,选定它为根,它的子树结构都相同,且它的度大于1,找到最大度的那个好的点,并输出它的最大度。
题解:典型换根dp,但是如何判断子树结构是否一致,这个时候就需要树hash来判断,注意:当我们hash合并的时候,需要按照子树的hash值的大小来合并,这样才是正确的合并顺序。
#include<bits/stdc++.h>
#define ll long long
const int maxn = 4e3+10,mod1=1e9+7,mod2=998244353;
ll p1[maxn],p2[maxn];
using namespace std;
vector<int>G[maxn];
struct node {
int h1,h2,len;
}dp[maxn];
vector<node>sf[maxn];
int ans = -1;
bool cmp(int a,int b)
{
if(dp[a].h1==dp[b].h1) return dp[a].h2<dp[a].h2;
return dp[a].h1<dp[b].h1;
}
node merge(node a,node b)
{
node tmp = {0,0,a.len+b.len};
tmp.h1 = (1LL*a.h1*p1[b.len]+b.h1)%mod1;
tmp.h2 = (1LL*a.h2*p2[b.len]+b.h2)%mod2;
return tmp;
}
void dfs(int u,int fa)
{
dp[u] = node{1,1,1};
for(auto v:G[u])
if(v!=fa) dfs(v,u);
sort(G[u].begin(),G[u].end(),cmp);
for(auto v:G[u])
if(v!=fa) dp[u] = merge(dp[u],dp[v]);
}
void dfs2(int u,int fa)
{
dp[u] = node{1,1,1};
sf[u].push_back(dp[u]);
sort(G[u].begin(),G[u].end(),cmp);
node tmp = node{0,0,0};
int flag = 1;
for(auto v:G[u])
{
dp[u] = merge(dp[u],dp[v]);
if(tmp.len==0) tmp = dp[v];
else if(dp[v].h1!=tmp.h1||dp[v].h2!=tmp.h2)
flag = 0;
sf[u].push_back(dp[u]);
}
if(flag&&G[u].size()>1)
ans = max(ans,(int)G[u].size());
node tp = node{0,0,0};
for(int i = G[u].size()-1;i>=0;i--)
{
dp[u] = merge(sf[u][i],tp);
tp = merge(tp,dp[G[u][i]]);
if(G[u][i]!=fa) dfs2(G[u][i],u);
}
}
int main()
{
p1[0] = p2[0] = 1;
for(int i=1;i<maxn;i++)
{
p1[i] = 1LL * p1[i-1] * 199997 % mod1;
p2[i] = 1LL * p2[i-1] * 199999 % mod2;
}
int n,u,v;scanf("%d",&n);
for(int i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1,0);
dfs2(1,0);
printf("%d\n",ans);
}