zoukankan      html  css  js  c++  java
  • [JZOJ3296] 【SDOI2013】刺客信条

    题目

    题目大意

    给你一棵树,树上每个节点有0011的状态。
    用最少的操作次数使得当前状态与目标状态同构。


    思考历程

    首先想到的是找重心。
    因为根是不确定的,但重心只会有一个或两个,以重心为根就能方便很多。
    如果重心有两个,就将连接它们的边拆成点,让它们分别与这个点相连就好了(重心是连在一起的)。
    然后就是树上哈希……
    哈希之后就开始了艰辛的思考历程……
    于是比赛就结束了。


    正解

    我前面的思考是没有问题的。
    后面的该怎么处理?当然是DP啊!
    fi,jf_{i,j}表示子树ii对应子树jj的最小操作数。
    显然如果ii能匹配jj,就必须要保证它们的哈希值相等。
    但是还会有其它条件,综合起来就是它们祖先的哈希值也相等……
    其实为了简化操作,我们再保证它们的深度相等就行了,虽然这并不能保证它们真的能够匹配,但也就算了吧……(我曾试过将完全出去这些冗余状态,但程序跑得更慢了,这意味着数据的这一类冗余状态并不多)
    所以可以将点按照深度和哈希值排序,从后往前做。
    接下来考虑转移,首先ai xor bja_i xor b_j是一定要加上的、
    然后就将各自的子树两两配对,也就是fx,ysum f_{x,y},其中xxii的儿子,yyjj的儿子,而且xxyy哈希值相等。
    我们要保证这个东西最小。
    于是这就变成了二分图的最小权完备匹配。
    题目说每个点的度数小于等于1111,这意味着看起来能够状压DP。
    但实际上,如果不非常用力地开常数,那状压DP是很难卡过去的……(我打了80分)。
    于是就可以用费用流或KM算法(因为这题求的是最小权,所以将边权取相反数就可以了)。
    时间复杂度为玄学……


    代码

    状压DP

    
    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 710
    #define mod 1000000007
    inline void get_min(int &a,int b){
    	a>b?a=b:0;
    }
    int n;
    int e[N][11],ne[N];
    int bz[N][11],nbz;
    int siz[N];
    int tmpr[2],cnt,root;
    void get_siz(int x,int fa){
    	bool is_root=1;
    	siz[x]=1;
    	for (int i=0;i<ne[x];++i){
    		int y=e[x][i];
    		if (y!=fa){
    			get_siz(y,x);
    			siz[x]+=siz[y];
    			if (y!=fa)
    				is_root&=(siz[y]<<1<=n);
    		}
    	}
    	is_root&=(n-siz[x]<<1<=n);
    	if (is_root)
    		tmpr[cnt++]=x;
    }
    int fa[N],dep[N];
    long long powd[N],h[N];
    inline bool cmpe(int x,int y){
    	return siz[x]<siz[y] || siz[x]==siz[y] && h[x]<h[y];
    }
    void get_hash(int x){
    	dep[x]=dep[fa[x]]+1;
    	siz[x]=1;
    	for (int i=0;i<ne[x];++i){
    		int y=e[x][i];
    		if (y!=fa[x]){
    			fa[y]=x;
    			get_hash(y);
    			siz[x]+=siz[y];
    		}
    		else{
    			for (int j=i;j<ne[x]-1;++j)
    				e[x][j]=e[x][j+1];
    			ne[x]--;
    			--i;
    		}
    	}
    	sort(e[x],e[x]+ne[x],cmpe);
    	h[x]=siz[x];
    	int w=1;
    	for (int i=0;i<ne[x];++i){
    		int y=e[x][i];
    		h[x]=(h[x]+h[y]*powd[w])%mod;
    		w+=siz[y];
    	}
    }
    int q[N];
    inline bool cmpq(int x,int y){
    	return dep[x]<dep[y] || dep[x]==dep[y] && h[x]<h[y];
    }
    int f[N][N],g[13][2048];
    //int bg[13][13];
    int a[N],b[N];
    int main(){
    	freopen("in.txt","r",stdin);
    	scanf("%d",&n);
    	for (int i=1;i<n;++i){
    		int u,v;
    		scanf("%d%d",&u,&v);
    		e[u][ne[u]++]=v;
    		e[v][ne[v]++]=u;
    	}
    	for (int i=1;i<=n;++i)
    		scanf("%d",&a[i]);
    	for (int i=1;i<=n;++i)
    		scanf("%d",&b[i]);
    	get_siz(1,0);
    	if (cnt==2){
    		int u=tmpr[0],v=tmpr[1];
    		root=++n;
    		for (int i=0;i<ne[u];++i)
    			if (e[u][i]==v){
    				e[u][i]=root;
    				break;
    			}
    		for (int i=0;i<ne[v];++i)
    			if (e[v][i]==u){
    				e[v][i]=root;
    				break;
    			}
    		e[root][0]=u,e[root][1]=v,ne[root]=2;
    	}
    	else
    		root=tmpr[0];
    	powd[0]=1;
    	for (int i=1;i<=n;++i)
    		powd[i]=powd[i-1]*997%mod;
    	get_hash(root);
    	for (int i=1;i<=n;++i)
    		q[i]=i;
    	sort(q+1,q+n+1,cmpq);
    	memset(f,63,sizeof f);
    	for (int i=n,r=n,ii=q[i];i>=1;ii=q[--i]){
    		for (int j=i,jj=q[j];j>=1 && dep[ii]==dep[jj] && h[ii]==h[jj];jj=q[--j]){
    			memset(g,63,sizeof g);
    			g[0][0]=0;
    			for (int x=0;x<ne[ii];++x){
    				int xx=e[ii][x];
    				for (int k=0;k<1<<ne[ii];++k)
    					for (int y=0;y<ne[jj];++y){
    						if (k>>y&1)
    							continue;
    						get_min(g[x+1][k|1<<y],g[x][k]+f[xx][e[jj][y]]);
    					}
    			}
    			f[ii][jj]=g[ne[ii]][(1<<ne[ii])-1]+(a[ii]^b[jj]);
    			if (ii==jj)
    				continue;
    			memset(g,63,sizeof g);
    			g[0][0]=0;
    			for (int y=0;y<ne[jj];++y){
    				int yy=e[jj][y];
    				for (int k=0;k<1<<ne[jj];++k)
    					for (int x=0;x<ne[ii];++x){
    						if (k>>x&1)
    							continue;
    						get_min(g[y+1][k|1<<x],g[y][k]+f[yy][e[ii][x]]);
    					}
    			}
    			f[jj][ii]=g[ne[jj]][(1<<ne[jj])-1]+(a[jj]^b[ii]);
    		}
    	}
    	printf("%d
    ",f[root][root]);
    	return 0;
    }
    

    KM算法

    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 710
    #define mod 1000000007
    inline void get_min(int &a,int b){
    	a>b?a=b:0;
    }
    int n;
    int e[N][11],ne[N];
    int bz[N][11],nbz;
    int siz[N];
    int tmpr[2],cnt,root;
    void get_siz(int x,int fa){
    	bool is_root=1;
    	siz[x]=1;
    	for (int i=0;i<ne[x];++i){
    		int y=e[x][i];
    		if (y!=fa){
    			get_siz(y,x);
    			siz[x]+=siz[y];
    			if (y!=fa)
    				is_root&=(siz[y]<<1<=n);
    		}
    	}
    	is_root&=(n-siz[x]<<1<=n);
    	if (is_root)
    		tmpr[cnt++]=x;
    }
    int fa[N],dep[N];
    long long powd[N],h[N];
    inline bool cmpe(int x,int y){
    	return siz[x]<siz[y] || siz[x]==siz[y] && h[x]<h[y];
    }
    void get_hash(int x){
    	dep[x]=dep[fa[x]]+1;
    	siz[x]=1;
    	for (int i=0;i<ne[x];++i){
    		int y=e[x][i];
    		if (y!=fa[x]){
    			fa[y]=x;
    			get_hash(y);
    			siz[x]+=siz[y];
    		}
    		else{
    			for (int j=i;j<ne[x]-1;++j)
    				e[x][j]=e[x][j+1];
    			ne[x]--;
    			--i;
    		}
    	}
    	sort(e[x],e[x]+ne[x],cmpe);
    	h[x]=siz[x];
    	int w=1;
    	for (int i=0;i<ne[x];++i){
    		int y=e[x][i];
    		h[x]=(h[x]+h[y]*powd[w])%mod;
    		w+=siz[y];
    	}
    }
    int q[N];
    inline bool cmpq(int x,int y){
    	return dep[x]<dep[y] || dep[x]==dep[y] && h[x]<h[y];
    }
    int f[N][N];
    int m;
    int bg[13][13];
    int exl[13],exr[13];
    bool visl[13],visr[13];
    int bel[13];
    bool find(int x){
    	visl[x]=1;
    	for (int i=0;i<m;++i)
    		if (exl[x]+exr[i]==bg[x][i] && !visr[i]){
    			visr[i]=1;
    			if (bel[i]==-1 || find(bel[i])){
    				bel[i]=x;
    				return 1;
    			}
    		}
    	return 0;
    }
    inline int km(){
    	memset(bel,255,sizeof bel);
    	for (int i=0;i<m;++i){
    		exl[i]=bg[i][0];
    		for (int j=1;j<m;++j)
    			exl[i]=max(exl[i],bg[i][j]);
    	}
    	memset(exr,0,sizeof exr);
    	for (int k=0;k<m;++k){
    		while (1){
    			memset(visl,0,sizeof visl);
    			memset(visr,0,sizeof visr);
    			if (find(k))
    				break;
    			int d=0x3f3f3f3f;
    			for (int i=0;i<m;++i)
    				if (visl[i])
    					for (int j=0;j<m;++j)
    						if (!visr[j])
    							d=min(d,exl[i]+exr[j]-bg[i][j]);
    			for (int i=0;i<m;++i)
    				if (visl[i])
    					exl[i]-=d;
    			for (int j=0;j<m;++j)
    				if (visr[j])
    					exr[j]+=d;
    		}
    	}
    	int res=0;
    	for (int i=0;i<m;++i)
    		res+=bg[bel[i]][i];
    	return -res;
    }
    int a[N],b[N];
    inline void calc(int ii,int jj){
    	for (int x=0;x<ne[ii];++x){
    		int xx=e[ii][x];
    		for (int y=0;y<ne[jj];++y)
    			bg[x][y]=-f[xx][e[jj][y]];
    	}
    	m=ne[ii];
    	f[ii][jj]=km()+(a[ii]^b[jj]);
    }
    int main(){
    	freopen("in.txt","r",stdin);
    	scanf("%d",&n);
    	for (int i=1;i<n;++i){
    		int u,v;
    		scanf("%d%d",&u,&v);
    		e[u][ne[u]++]=v;
    		e[v][ne[v]++]=u;
    	}
    	for (int i=1;i<=n;++i)
    		scanf("%d",&a[i]);
    	for (int i=1;i<=n;++i)
    		scanf("%d",&b[i]);
    	get_siz(1,0);
    	if (cnt==2){
    		int u=tmpr[0],v=tmpr[1];
    		root=++n;
    		for (int i=0;i<ne[u];++i)
    			if (e[u][i]==v){
    				e[u][i]=root;
    				break;
    			}
    		for (int i=0;i<ne[v];++i)
    			if (e[v][i]==u){
    				e[v][i]=root;
    				break;
    			}
    		e[root][0]=u,e[root][1]=v,ne[root]=2;
    	}
    	else
    		root=tmpr[0];
    	powd[0]=1;
    	for (int i=1;i<=n;++i)
    		powd[i]=powd[i-1]*997%mod;
    	get_hash(root);
    	for (int i=1;i<=n;++i)
    		q[i]=i;
    	sort(q+1,q+n+1,cmpq);
    	memset(f,63,sizeof f);
    	for (int i=n,r=n,ii=q[i];i>=1;ii=q[--i]){
    		for (int j=i,jj=q[j];j>=1 && dep[ii]==dep[jj] && h[ii]==h[jj];jj=q[--j]){
    			calc(ii,jj);
    			if (ii!=jj)
    				calc(jj,ii);
    		}
    	}
    	printf("%d
    ",f[root][root]);
    	return 0;
    }
    

    总结

    想不出来的东西就用网络流吧……

  • 相关阅读:
    document.getElementById("mytxt").style.left=""style.left在IE的FF中注意
    asp.net 用户控件中 使用相对路径的解决方法 图片路径问题(用户控件、图片路径) ,ResolveUrl
    探索 Block (一) (手把手讲解Block 底层实现原理)
    iOS 多线程开发 (概念与API简介)
    iOS 性能小点
    iOS runtime (二)(runtime学习之AutoCoding源码分析)
    探索 NSRunLoop (二)(NSRunLoop 自己动手实现SimpleRunLoop)
    iOS NSNotificationCenter (自己实现一个通知中心XMCNotificationCenter)
    iOS runtime (三)(runtime学习之YYModel源码分析)
    iOS runtime(一)(runtime 分析理解)
  • 原文地址:https://www.cnblogs.com/jz-597/p/11145215.html
Copyright © 2011-2022 走看看