P6204 [USACO07CHN] Treasure G 题解

· · 题解

省流题意

给定一棵基环树,求以每个节点为根时共有多少种不同构的树。

题目分析

如果这是一颗树的话,那么直接树哈希,换根 DP 即可,在此不再赘述,详见板子题。

现在我们需要考虑的是如何将这个环也哈希进来。这时我们发现,环与孩子的唯一区别就是前者有序而后者无序,所以我们只需要采取字符串哈希的形式即可。又因为这是一个环,所以顺时针和逆时针没有区别,我们需要将两个方向上的哈希值相加。(rg[i] 代表环上的第 i 个点)

    ull now=0;
    for(int i=2;i<=cnt;i++)
        now=now*331+Hash[rg[i]];
    h[rg[1]]=now+Hash[rg[1]];
    for(int i=2;i<=cnt;i++){
        now-=Hash[rg[i]]*pow331[cnt-1];
        now=now*331+Hash[rg[i-1]];
        h[rg[i]]=now+Hash[rg[i]];
    }
    now=0;
    for(int i=cnt;i>=2;i--)
        now=now*331+Hash[rg[i]];
    h[rg[1]]+=now;
    for(int i=cnt;i>=2;i--){
        now-=Hash[rg[i]]*pow331[cnt-1];
        if(i==cnt)now=now*331+Hash[rg[1]];
        else now=now*331+Hash[rg[i+1]];
        h[rg[i]]+=now;
    }

这样就求出来了环上点的哈希值,其他的点正常求即可。

代码:

#include<bits/stdc++.h>
using namespace std;
inline void rd(){}
template<typename T,typename ...U>
inline void rd(T &x,U &...args){
    int f=1;x=0;char c=getchar();
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c))x=(x<<3)+(x<<1)+c-'0',c=getchar();
    x*=f,rd(args...);
}

typedef unsigned long long ull;
const ull mk=std::chrono::steady_clock::now().time_since_epoch().count();
ull Shift(ull x){x^=mk;x^=x<<13;x^=x>>7;x^=x<<17;x^=mk;return x;}
const int N=1e5+5;
int fst[N],nxt[N<<1],v[N<<1],idx,ans;
int n,vis[N],ring[N<<1],rg[N],deg[N],cnt;
inline void Add(int a,int b){
    v[idx]=b,nxt[idx]=fst[a];
    fst[a]=idx++;
}
int DFS(int x,int f){
    vis[x]=1;
    for(int i=fst[x];~i;i=nxt[i]){
        int y=v[i];
        if(y==f)continue;
        if(vis[y])return ring[i]=ring[i^1]=1,rg[++cnt]=y,1;
        if(DFS(y,x)){
            ring[i]=ring[i^1]=1;
            rg[++cnt]=y;
            if(rg[1]==x)return 0;
            return 1;
        }
    }
    return 0;
}
ull Hash[N],pow331[N],h[N];
set<ull> str,stt;
void Get_Hash(int x,int f){
    Hash[x]=h[x];
    for(int i=fst[x];~i;i=nxt[i]){
        int y=v[i];
        if(ring[i]||y==f)continue;
        Get_Hash(y,x);
        Hash[x]+=Shift(Hash[y]);
    }
}
inline void Solve_Ring(){
    pow331[1]=1;
    Get_Hash(rg[1],rg[1]);
    for(int i=2;i<=cnt;i++){
        Get_Hash(rg[i],rg[i]);
        pow331[i]=pow331[i-1]*331;
    }
    ull now=0;
    for(int i=2;i<=cnt;i++)
        now=now*331+Hash[rg[i]];
    h[rg[1]]=now+Hash[rg[1]];
    for(int i=2;i<=cnt;i++){
        now-=Hash[rg[i]]*pow331[cnt-1];
        now=now*331+Hash[rg[i-1]];
        h[rg[i]]=now+Hash[rg[i]];
    }
    now=0;
    for(int i=cnt;i>=2;i--)
        now=now*331+Hash[rg[i]];
    h[rg[1]]+=now;
    for(int i=cnt;i>=2;i--){
        now-=Hash[rg[i]]*pow331[cnt-1];
        if(i==cnt)now=now*331+Hash[rg[1]];
        else now=now*331+Hash[rg[i+1]];
        h[rg[i]]+=now;
    }
    for(int i=1;i<=cnt;i++)if(deg[rg[i]]<4)str.insert(h[rg[i]]);
    ans+=str.size();
}
void Get_Hashes(int x,int f){
    for(int i=fst[x];~i;i=nxt[i]){
        int y=v[i];
        if(ring[i]||y==f)continue;
        h[y]=Hash[y]+Shift(h[x]-Shift(Hash[y]));
        if(deg[y]<4)stt.insert(h[y]);
        Get_Hashes(y,x);
    }
}
inline void Solve_Tree(){
    for(int i=1;i<=cnt;i++){
        Get_Hash(rg[i],rg[i]);
        Get_Hashes(rg[i],rg[i]);
    }
    ans+=stt.size();
}
int main(){
    memset(fst,-1,sizeof fst);
    rd(n);
    for(int i=1;i<=n;i++){
        h[i]=1;
        int x,y;rd(x,y);
        Add(x,y);Add(y,x);
        deg[y]++,deg[x]++;
    }
    DFS(1,1);
    Solve_Ring();
    Solve_Tree();
    printf("%d\n",ans);
    return 0;
}