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

    总结

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

  • 相关阅读:
    Windows CMD中 find命令(字符串查找)
    网络地址转换静态NAT
    网络地址转换静态NAT
    Android 的暗示 hint 用法
    Android 的暗示 hint 用法
    SQL Server 扩展事件
    SQL Server 扩展事件
    SqlServer中Exists的使用
    SqlServer中Exists的使用
    数据库还原,System.Data.SqlClient.SqlError: 因为数据库正在使用,所以无法获得对数据库的独占访问权。
  • 原文地址:https://www.cnblogs.com/jz-597/p/11145215.html
Copyright © 2011-2022 走看看