CodeForces1252 F.Regular Forestation(换根dp+树Hash判断树同构)

题意:

给定n个点的无根树,点x是好点当且仅当:
1.x的度数大于1
2.以x为根时,x的所有子树结构相同

找到度数最大的好点,输出这个最大度数

数据范围:n<=4e3

解法:

枚举每个点作为根,计算子树是否同构即可,同构可以用树Hash来做

枚举部分用换根dp优化

ps:
我咋感觉这道题我写出来的换根和以前的换根写法不太一样。。

code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=4e3+5;
//双Hash,且质数和模数都不同
const int p1=131,p2=1331;
const int mod1=1e9+7,mod2=998244353;
int base1[maxm],base2[maxm];
void H_init(){
    base1[0]=base2[0]=1;
    for(int i=1;i<maxm;i++){
        base1[i]=base1[i-1]*p1%mod1;
        base2[i]=base2[i-1]*p2%mod2;
    }
}
struct H{
    int h1,h2,len;
    friend bool operator<(H a,H b){
        if(a.h1!=b.h1)return a.h1<b.h1;
        if(a.h2!=b.h2)return a.h2<b.h2;
        return a.len<b.len;
    }
    bool eq(H a){
        return h1==a.h1&&h2==a.h2&&len==a.len;
    }
    H merge(H a){
        H ans;
        ans.h1=(h1*base1[a.len]%mod1+a.h1)%mod1;
        ans.h2=(h2*base2[a.len]%mod2+a.h2)%mod2;
        ans.len=len+a.len;
        return ans;
    }
};
//
vector<int>g[maxm];
int n;
//
H d[maxm];
H last[maxm];
bool cmp(int a,int b){//按Hash值排序,这样可以保证相同构树合并子树得到的Hash值相同
    return d[a]<d[b];
}
int ans=-1;
//
void dfs(int x,int fa){//只合并子树节点
    for(int v:g[x]){
        if(v==fa)continue;
        dfs(v,x);
    }
    d[x]={1,1,1};
    sort(g[x].begin(),g[x].end(),cmp);
    for(int v:g[x]){
        if(v==fa)continue;
        d[x]=d[x].merge(d[v]);
    }
}
void dfs2(int x,int fa){//合并父节点
    //假设x是根,尝试用x更新答案
    int ok=1;
    H temp={0,0,0};//{0,0,0}表示空
    for(int v:g[x]){//判断子树是否同构
        if(temp.len==0)temp=d[v];
        else if(!d[v].eq(temp)){
            ok=0;break;
        }
    }
    if(ok&&g[x].size()>1){
        ans=max(ans,(int)g[x].size());
    }
    //
    vector<H>order;//存点x的子节点Hash合并顺序
    for(int v:g[x]){//d[fa]也被放进去了
        order.push_back(d[v]);
    }
    vector<H>st=order;//st[i]表示以点i为起点的Hash值
    vector<H>ed=order;//ed[i]表示以点i为终点的Hash值
    for(int i=(int)order.size()-2;i>=0;i--){
        st[i]=st[i].merge(st[i+1]);
    }
    for(int i=1;i<(int)order.size();i++){
        ed[i]=ed[i-1].merge(ed[i]);
    }
    //
    int len=g[x].size();
    for(int i=0;i<len;i++){
        int v=g[x][i];
        if(v==fa)continue;
        H f={1,1,1};
        if(i>0)f=f.merge(ed[i-1]);
        if(i<len-1)f=f.merge(st[i+1]);
        //这时候f是当v为根时,v->x的Hash值
        d[x]=f;//将d[x]替换为f,这样检查v的时候可以直接假设v是根
        dfs2(v,x);
    }
}
signed main(){
    H_init();
    cin>>n;
    for(int i=1;i<n;i++){
        int a,b;cin>>a>>b;
        g[a].push_back(b);
        g[b].push_back(a);
    }
    dfs(1,0);
    d[0]={0,0,0};
    dfs2(1,0);
    cout<<ans<<endl;
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值