JSOI2016 独特的树叶

a 夏天 提交于 2019-12-06 14:30:49

JSOI 2016 独特的树叶

题面描述

有两颗大小分别为\(n\)\(n+1\)的树,问删除后者的一个叶子,两者能否同构。如果能,输出最小的叶子标号使得删去这个叶子两者同构。

数据范围:\(n\le 10^5\)

思路

用一些玄学方法把第一棵树每个点及其子树\(hash\)起来,再\(up-down\)一下,求出每个点作为根这整棵树的\(hash\)值,存入\(map\)中。

再用类似的方法操作第二棵树即可。

关于树哈希的方法,建议使用异或,这样不仅撤销方便,而且无视顺序,无需排序。

代码

#include<bits/stdc++.h>
#define pii pair<int,int>
#define mkp(x,y) make_pair(x,y)
#define go(x) for(int i=head[x];i;i=edge[i].nxt)
#define now edge[i].v
using namespace std;
const int sz=1e5+7;
const int p1=1e8+7;
const int p2=998244353;
const int q1=52437;
const int q2=9813475;
int n;
int ans;
int u,v,cnt;
int r[sz];
int siz[sz];
int head[sz];
int w[sz][2];
int hs[sz][2];
map<pii,int>mp;
struct Edge{
    int v,nxt;
}edge[sz<<1];
void make_edge(int u,int v){
    edge[++cnt]=(Edge){v,head[u]};head[u]=cnt;
    edge[++cnt]=(Edge){u,head[v]};head[v]=cnt; 
}
void dfs(int x,int fa){
    siz[x]=1;
    go(x) if(now!=fa){
        dfs(now,x);
        siz[x]+=siz[now];
        hs[x][0]^=1ll*w[siz[now]][0]*hs[now][0]%p1;
        hs[x][1]^=1ll*w[siz[now]][1]*hs[now][1]%p2;
    }
    hs[x][0]^=siz[x];
    hs[x][1]^=siz[x];
}
void Dfs(int x,int fa,int n0,int n1){
    mp[mkp(n0,n1)]=1;
    int k0,k1,t0,t1;
    go(x) if(now!=fa){
        k0=n0^n^(n-siz[now])^(1ll*w[siz[now]][0]*hs[now][0]%p1);
        k1=n1^n^(n-siz[now])^(1ll*w[siz[now]][1]*hs[now][1]%p2);
        t0=hs[now][0]^siz[now]^n^(1ll*w[n-siz[now]][0]*k0%p1);
        t1=hs[now][1]^siz[now]^n^(1ll*w[n-siz[now]][1]*k1%p2);
        Dfs(now,x,t0,t1);
    }
}
void getans(int x,int fa,int n0,int n1){
    int k0,k1,t0,t1;
    go(x) if(now!=fa){
        if(siz[now]==1){
            t0=n0^n^(n-1)^(1ll*w[siz[now]][0]*hs[now][0]%p1);
            t1=n1^n^(n-1)^(1ll*w[siz[now]][1]*hs[now][1]%p2);
            if(mp.find(mkp(t0,t1))!=mp.end()) ans=min(ans,now);
            continue;
        }
        k0=n0^n^(n-siz[now])^(1ll*w[siz[now]][0]*hs[now][0]%p1);
        k1=n1^n^(n-siz[now])^(1ll*w[siz[now]][1]*hs[now][1]%p2);
        t0=hs[now][0]^siz[now]^n^(1ll*w[n-siz[now]][0]*k0%p1);
        t1=hs[now][1]^siz[now]^n^(1ll*w[n-siz[now]][1]*k1%p2);
        getans(now,x,t0,t1);
    }
}
int main(){
    scanf("%d",&n);
    w[0][0]=w[0][1]=1;
    for(int i=1;i<=n+1;i++){
        w[i][0]=1ll*w[i-1][0]*q1%p1;
        w[i][1]=1ll*w[i-1][1]*q2%p2;
    }
    for(int i=1;i<n;i++){
        scanf("%d%d",&u,&v);
        make_edge(u,v);
    }
    dfs(1,0);
    Dfs(1,0,hs[1][0],hs[1][1]);
    n++;
    cnt=0;
    ans=INT_MAX;
    memset(head,0,sizeof(head));
    memset(hs,0,sizeof(hs));
    for(int i=1;i<n;i++){
        scanf("%d%d",&u,&v);
        r[u]++,r[v]++;
        make_edge(u,v);
    }
    dfs(1,0);
    getans(1,0,hs[1][0],hs[1][1]);
    if(r[1]==1){
        int v=edge[head[1]].v;
        if(mp.find(mkp(hs[v][0],hs[v][1]))!=mp.end()) ans=min(ans,1);
    }
    if(ans!=INT_MAX) printf("%d\n",ans);
    else puts("-1");
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!