F. Regular Forestation 树hash+换根dp

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);
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值