1 条题解

  • 0
    @ 2025-8-24 22:18:30

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar luanyanjia
    菜 -我ら是个と大に傻む逼なり-

    搬运于2025-08-24 22:18:30,当前版本为作者最后更新于2024-05-23 21:57:06,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    省流题意

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

    题目分析

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

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

    	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;
    } 
    
    • 1

    信息

    ID
    5236
    时间
    1000ms
    内存
    125MiB
    难度
    (无)
    标签
    递交数
    0
    已通过
    0
    上传者