【bzoj3197】【SDOI2013】【assassin】

3197: [Sdoi2013]assassin

Time Limit: 10 Sec Memory Limit: 256 MB
Submit: 221 Solved: 113
[Submit][Status][Discuss]
Description

【bzoj3197】【SDOI2013】【assassin】_第1张图片

Input

【bzoj3197】【SDOI2013】【assassin】_第2张图片

Output

这里写图片描述

Sample Input

4

1 2

2 3

3 4

0 0 1 1

1 0 0 0 

Sample Output

1

HINT

【bzoj3197】【SDOI2013】【assassin】_第3张图片

f[i][j] 表示i根j对应时的最小代价。这样树形dp就行了,从下往上更新。
在更新的时候,就相当于是这样一个问题,一些点可以和另一些点对应,每种对应都有相应的价值,问最小的价值。这很明显可以网络流。
如果x的一个儿子i可以和j对应,那么就在这两个点之间连一条 (1,f[i][j]) 的边,跑一边费用流,就是当前状态的答案。
怎样判断两个点能否对应呢??
可以hash这个树,hash相同的节点就是可以对应的节点。
还有一个问题:题中给的是一个无根树,如果随便选一个节点当根hash的话,呢么选的节点不同hash值会不一样。
考虑这是一棵树,那么就可以把树的重心当根,这样树就相当于被比较平均的分开了,再hash就没什么问题了。如果这个树有两个重心的话,就再新建一个节点,分别向两个重心连边,把这个点当成重心做就行了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<ctime>
using namespace std;
#define LL unsigned int
#define p 233LL
#define D 1490
#define T 2*n+2
#define inf 707406378
const int N=1500;
const int M=500000;
bool use[N];
LL r[N],hash[N];
struct S{int st,en,va,co;}aa[M],bb[N];
int can,n,tot,siz[N],a[N],b[N],po[N],ne[N],point[N],next[M],l[N],dis[N],pre[N],s[N],root,o[5],f[N][N];
inline void add_1(int x,int y){
 ne[++tot]=po[x];po[x]=tot;
 bb[tot].st=x;bb[tot].en=y;
 ne[++tot]=po[y];po[y]=tot;
 bb[tot].st=y;bb[tot].en=x;
}
inline void get_root(int x,int last){
 int i;
 for(siz[x]=1,i=po[x];i;i=ne[i])
 if(bb[i].en!=last){
 get_root(bb[i].en,x);
 siz[x]+=siz[bb[i].en];
 s[x]=max(s[x],siz[bb[i].en]);
 }
 s[x]=max(s[x],n-siz[x]);
 if(s[root]==s[x]) o[++o[0]]=x;
 else if(s[root]>s[x]) root=x,o[o[0]=1]=x;
}
inline void get_hash(int x,int last){
 int i;
 LL sum=0;
 for(siz[x]=1,i=po[x];i;i=ne[i])
 if(bb[i].en!=last&&can!=i&&(can^1)!=i){
 get_hash(bb[i].en,x);
 sum+=hash[bb[i].en];
 siz[x]+=siz[bb[i].en];
 }
 hash[x]=(sum*(LL)p)^r[siz[x]];
}
inline void add_2(int x,int y,int va,int co){
 next[++tot]=point[x];point[x]=tot;
 aa[tot].st=x;aa[tot].en=y;aa[tot].va=va;aa[tot].co=co;
 next[++tot]=point[y];point[y]=tot;
 aa[tot].st=y;aa[tot].en=x;aa[tot].va=0;aa[tot].co=-co;
}
inline int SPFA(int x,int y){
 int h=0,t=1,u,i;
 memset(use,1,sizeof(use));
 memset(pre,0,sizeof(siz));
 memset(dis,127/3,sizeof(dis));
 l[t]=x;dis[x]=0;
 while(h!=t){
 h=h%D+1;u=l[h];use[u]=true;
 for(i=point[u];i;i=next[i])
 if(aa[i].va>0&&dis[aa[i].en]>dis[u]+aa[i].co){
 dis[aa[i].en]=dis[u]+aa[i].co;
 pre[aa[i].en]=i;
 if(use[aa[i].en]){
 use[aa[i].en]=false;
 t=t%D+1;
 l[t]=aa[i].en;
 }
 }
 }
 return dis[y];
}
inline int ISAP(int x,int y){
 int minn=inf,i;
 for(i=y;i!=x;i=aa[pre[i]].st)
 minn=min(minn,aa[pre[i]].va);
 for(i=y;i!=x;i=aa[pre[i]].st){
 aa[pre[i]].va-=minn;
 aa[pre[i]^1].va+=minn;
 }
 return minn;
}
inline void dp(int x,int last){
 int i,j,k;
 for(i=po[x];i;i=ne[i])
 if(bb[i].en!=last&&can!=i&&(can^1)!=i) dp(bb[i].en,x);
 for(i=1;i<=n;++i){
 if(hash[i]==hash[x]){
 memset(point,0,sizeof(point));
 for(tot=1,j=po[i];j;j=ne[j]) add_2(bb[j].en+n+1,T,1,0);
 for(j=po[x];j;j=ne[j])
 if(bb[j].en!=last&&can!=j&&(can^1)!=j){
 add_2(1,bb[j].en+1,1,0);
 for(k=po[i];k;k=ne[k])
 if(hash[bb[j].en]==hash[bb[k].en]) add_2(bb[j].en+1,bb[k].en+n+1,1,f[bb[j].en][bb[k].en]);
 }
 int minn=1,ans=0;
 while(minn!=inf){
 minn=SPFA(1,T);
 if(minn!=inf) ans+=minn*ISAP(1,T);
 }
 f[x][i]=ans+(a[x]!=b[i]);
 }
 }
}
int main(){
 srand(233);
 int i,x,y;
 scanf("%d",&n);
 memset(use,1,sizeof(use));
 for(tot=1,i=1;i<n;++i){
 scanf("%d%d",&x,&y);
 add_1(x,y);
 }
 for(i=1;i<=n;++i) scanf("%d",&a[i]);
 for(i=1;i<=n;++i) scanf("%d",&b[i]);
 s[0]=inf;get_root(1,0);
 if(o[0]==2){
 for(i=po[o[1]];i;i=ne[i])
 if(bb[i].en==o[2]){
 can=i;break;
 }
 root=++n;
 a[n]=b[n]=0;
 add_1(n,o[1]);
 add_1(n,o[2]);
 }
 for(i=1;i<=n;++i) r[i]=(LL)rand();
 get_hash(root,0);
 memset(f,127/3,sizeof(f));
 dp(root,0);
 int ans=inf;
 for(i=1;i<=n;++i)
 ans=min(ans,f[root][i]);
 printf("%d\n",ans);
}

你可能感兴趣的:(【bzoj3197】【SDOI2013】【assassin】)