zoukankan      html  css  js  c++  java
  • 【题解】【模板】树同构([BJOI2015]树的同构)

    【模板】树同构([BJOI2015]树的同构)

    ( ext{Solution:})

    由于月赛有一个和树同构相关的题目,所以来学一下树同构。

    这里不用树哈希的做法,考虑用括号序列。

    我们发现:当进入一个点的时候记录一个左括号,出去的时候记录一个右括号,这样会形成一个长度为 (2n) 的括号序列。

    同时,在没有标号的情况下,这种括号序列是可以确定树的结构的。

    但是我们观察到,当我们更改子树的拼接顺序,那么其括号序列就会改变。

    所以我们考虑求字典序最小的括号序列。容易证明两棵树同构当且仅当两棵树对应的最小括号序列相同。

    于是我们考虑直接暴力求这个东西。先把子树的最小表示求出来,然后暴力排序。复杂度 (O(n^2)) 因为排序的复杂度与 string 拼接的复杂度相比并不高。

    然后考虑无根树怎么比较。考虑转化为有根树,这样就可以直接用重心来做了,由于最多两个重心,这样就可以只比较两个点的最小表示。复杂度就是 (O(n^2 imes m)) 的了。

    代码里面封装了一个对树求最小表示的板子。

    #include<bits/stdc++.h>
    using namespace std;
    typedef double db;
    //#define int long long
    const int mod=1e9+7;
    const db eps=1e-10;
    inline int Max(int x,int y){return x>y?x:y;}
    inline int Min(int x,int y){return x<y?x:y;}
    inline db Max(db x,db y){return x-y>eps?x:y;}
    inline db Min(db x,db y){return x-y<eps?x:y;}
    inline int Add(int x,int y,int M=mod){return (x+y)%M;}
    inline int Mul(int x,int y,int M=mod){return 1ll*x*y%M;}
    inline int Dec(int x,int y,int M=mod){return (x-y+M)%M;}
    inline int Abs(int x){return x<0?-x:x;}
    inline int read(){
    	int s=0,w=1;
    	char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
    	while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
    	return s*w;
    }
    inline void write(int x){
    	if(x<0)putchar('-'),x=-x;
    	if(x>9)write(x/10);
    	putchar(x%10+'0');
    }
    inline int qpow(int x,int y){
    	int res=1;
    	while(y){
    		if(y&1)res=Mul(res,x);
    		x=Mul(x,x);y>>=1;
    	}
    	return res;
    }
    typedef pair<int,int> pr;
    #define fi first
    #define se second
    #define mk make_pair
    #define pb emplace_back
    #define poly vector<int>
    #define Bt(a) bitset<a>
    const int N=200;
    struct Tree_Construction{
    	int head[N],tot,n,siz[N],mx[N],Mi;
    	struct E{int nxt,to;}e[N];
    	string f[N],g[N],tmp;
    	inline void link(int x,int y){
    		e[++tot]=(E){head[x],y};
    		head[x]=tot;
    	}
    	void dfs(int x,int fa){
    		siz[x]=1;
    		for(int i=head[x];i;i=e[i].nxt){
    			int j=e[i].to;
    			if(j==fa)continue;
    			dfs(j,x);
    			siz[x]+=siz[j];
    			mx[x]=Max(mx[x],siz[j]);
    		}
    		mx[x]=Max(mx[x],n-siz[x]);
    		Mi=Min(Mi,mx[x]);
    	}
    	void Show(int x,int fa){
    		f[x]="0";
    		for(int i=head[x];i;i=e[i].nxt){
    			int j=e[i].to;
    			if(j==fa)continue;
    			Show(j,x);
    		}
    		int cnt=0;
    		for(int i=head[x];i;i=e[i].nxt){
    			int j=e[i].to;
    			if(j==fa)continue;
    			g[++cnt]=f[j];
    		}
    		sort(g+1,g+cnt+1);
    		for(int i=1;i<=cnt;++i)f[x]+=g[i];
    		f[x]+="1";
    	}
    	void Init(){
    		n=read();
    		for(int i=1;i<=n;++i){
    			int u=read();
    			if(!u)continue;
    			link(i,u);link(u,i);
    		}
    	}
    	string Get(){
    		Init();
    		Mi=n+1;
    		dfs(1,0);
    		tmp="1";
    		for(int i=1;i<=n;++i){
    			if(mx[i]==Mi){
    				Show(i,0);
    				tmp=min(tmp,f[i]);
    			}
    		}
    		return tmp;
    	}
    }tr[N];
    int T;
    string mn[N];
    int main(){
    	T=read();
    	for(int i=1;i<=T;++i){
    		mn[i]=tr[i].Get();
    		for(int j=1;j<=i;++j){
    			if(mn[j]==mn[i]){
    				printf("%d
    ",j);
    				break;
    			}
    		}
    	}
    	return 0;
    }
    
    

    树哈希:

    考虑一种树哈希的方式,比最小表示法的复杂度要低。也可以直接用基数排序降低复杂度。

    考虑对一棵有根树哈希如何来做。给出下列公式:

    (p_i) 表示第 (i) 个质数,(siz[i]) 表示以 (i) 为根的子树的大小,(f[x]) 表示以 (x) 为根的子树的哈希值。那么:

    [f[u]=sum_{vin son[u]} f[v] imes p_{siz[v]} ]

    这样就可以直接用 (O(n)) 处理出一棵有根树的哈希值了。

    考虑如何求无根树,用换根 (dp.)(g[u]) 为以 (u) 为根的全树的哈希值,那么:

    [g[u]=(g[fa]-f[u] imes p_{siz[x]}) imes p_{n-siz[x]}+f[x] ]

    这样就可以处理好无根树的情况了。

    本题直接用了 set 维护所有树的哈希值。其实也可以直接求重心做两遍 (dp) 啥的,但还是换根方便一些。

    注意要预处理好前 (n) 个质数。

    #include<bits/stdc++.h>
    using namespace std;
    typedef double db;
    //#define int long long
    const int mod=1e9+7;
    const db eps=1e-14;
    inline int Max(int x,int y){return x>y?x:y;}
    inline int Min(int x,int y){return x<y?x:y;}
    inline db Max(db x,db y){return x-y>eps?x:y;}
    inline db Min(db x,db y){return x-y<eps?x:y;}
    inline int Add(int x,int y,int M=mod){return (x+y)%M;}
    inline int Mul(int x,int y,int M=mod){return 1ll*x*y%M;}
    inline int Dec(int x,int y,int M=mod){return (x-y+M)%M;}
    inline int Abs(int x){return x<0?-x:x;}
    inline int read(){
    	int s=0,w=1;
    	char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
    	while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
    	return s*w;
    }
    inline void write(int x){
    	if(x<0)putchar('-'),x=-x;
    	if(x>9)write(x/10);
    	putchar(x%10+'0');
    }
    inline int qpow(int x,int y){
    	int res=1;
    	while(y){
    		if(y&1)res=Mul(res,x);
    		x=Mul(x,x);y>>=1;
    	}
    	return res;
    }
    typedef pair<int,int> pr;
    #define fi first
    #define se second
    #define mk make_pair
    #define pb emplace_back
    #define poly vector<int>
    const int N=500;
    const int SN=40000;
    namespace Refined_heart{
    	typedef unsigned long long ull;
    	int p[SN],cnt,vis[SN];
    	ull f[N][N],g[N][N];
    	void Euler(){
    		for(int i=2;i<SN;++i){
    			if(!vis[i])p[++cnt]=i;
    			for(int j=1;j<=cnt&&i*p[j]<SN;++j){
    				vis[i*p[j]]=1;
    				if(i%p[j]==0)break; 
    			}
    		}
    	} 
    	poly G[N][N];
    	inline void link(int x,int y,int pos){G[pos][x].pb(y);}
    	int T,n,siz[N][N];
    	void dfs1(int x,int fa,int pos){
    		siz[pos][x]=1;f[pos][x]=1;
    		for(auto j:G[pos][x]){
    			if(j==fa)continue;
    			dfs1(j,x,pos);
    			siz[pos][x]+=siz[pos][j];
    			f[pos][x]+=1ull*f[pos][j]*p[siz[pos][j]];
    		}
    	}
    	void dfs2(int x,int fa,int pos){
    		g[pos][x]=(g[pos][fa]-f[pos][x]*p[siz[pos][x]])*p[siz[pos][1]-siz[pos][x]]+f[pos][x];
    		for(auto v:G[pos][x]){
    			if(v==fa)continue;
    			dfs2(v,x,pos);
    		}
    	}
    	void sol(int pos){
    		n=read();
    		for(int i=1;i<=n;++i){
    			int u=read();
    			if(!u)continue;
    			link(u,i,pos);link(i,u,pos);
    		}
    		dfs1(1,0,pos);
    		dfs2(1,0,pos);
    	}
    	void solve(){
    		Euler();
    		T=read();
    		for(int i=1;i<=T;++i)sol(i);
    		for(int i=1;i<=T;++i){
    			set<ull>s;
    			s.clear();
    			for(int j=1;j<=siz[i][1];++j)s.insert(g[i][j]);
    			for(int j=1,k=0;j<=T;++j){
    				for(int l=1;l<=siz[j][1];++l){
    					if(siz[i][1]==siz[j][1]&&s.find(g[j][l])!=s.end()){
    						k=1;
    						printf("%d
    ",j);
    						break;
    					}
    				}
    				if(k)break;
    			}
    		}
    	}
    }
    int main(){
    	Refined_heart::solve();
    	return 0;
    }
    
  • 相关阅读:
    http请求的GET和POST请求:查询和新增(server.php)
    http请求的GET和POST请求:查询和新增(ajax)
    shell中$(( )) 与 $( ) 还有${ }的区别
    linux查看操作系统版本信息
    Linux useradd 与 adduser的区别, /sbin/nologin 与 /bin/bash
    红帽触摸屏屏幕180旋转
    Sed命令n,N,d,D,p,P,h,H,g,G,x解析3
    sed命令
    sed高级用法:模式空间(pattern space)和保持空间(hold space)
    sed的模式空间和保持空间
  • 原文地址:https://www.cnblogs.com/h-lka/p/15491741.html
Copyright © 2011-2022 走看看