bzoj 3197 [Sdoi2013]assassin(Hash+DP+KM)
Description
Input
Output
Sample Input
4
1 2
2 3
3 4
0 0 1 1
1 0 0 0
1 2
2 3
3 4
0 0 1 1
1 0 0 0
Sample Output
1
HINT
【思路】
Hash,DP,KM
题目就是要找一个同构的树,使能够以最少的修改转换成目标状态。
树的形态可以有多种但是他的中心只有一个。先找出中心,如果在边上则新建一个节点。以中心为根建树。同构的节点在树上是对称的。求出Hash。Hash函数如下:
H[u]=((((A*H[son1])*p+H[son2])*p+H[son3])*p)
通过判断hash值和节点深度dep就可知道是否同构。
在树上进行DP,设F[i][j]表示将j子树作为i子树同构时对应的最小花费。转移就是将i和j两个节点的儿子以最小权和进行完美匹配,匹配代价为F[soni][sonj],所以我们要按照dep序和hash将节点排一下序,这样相同的hash形成了一个区间,对于一个区间内的所有点对可以求出F,同时在求当前F的时候子结点的信息已经得到。
因为形态唯一,所以最终答案为F[root][root]。
【代码】
1 #include<cmath> 2 #include<queue> 3 #include<vector> 4 #include<cstdio> 5 #include<cstring> 6 #include<iostream> 7 #include<algorithm> 8 #define FOR(a,b,c) for(int a=(b);a<=(c);a++) 9 using namespace std; 10 11 typedef long long LL; 12 const int N = 805; 13 const int INF = 1e9; 14 const int A = 14221; 15 const int B = 20707; 16 const int P = 4481; 17 const int MOD = 1060469; 18 19 struct Edge { int v,flag; 20 }; 21 void read(int& x) { 22 char c=getchar(); int f=1; x=0; 23 while(!isdigit(c)) {if(c=='-')f=-1; c=getchar();} 24 while(isdigit(c)) x=x*10+c-'0',c=getchar(); 25 x*=f; 26 } 27 28 vector<Edge> g[N]; 29 vector<int> tmp; 30 queue<int> q; 31 32 void adde(int u,int v) { 33 g[u].push_back((Edge){v,0}); 34 g[v].push_back((Edge){u,0}); 35 } 36 37 int n,root; 38 int dis[N],dep[N],fa[N],vis[N]; 39 int firstST[N],finalST[N]; 40 LL H[N]; 41 42 struct KM { 43 int g[N][N]; 44 int lx[N],rx[N]; 45 int l[N],r[N],res[N]; 46 int slack[N]; 47 48 void clear(int n) { 49 FOR(i,1,n) FOR(j,1,n) g[i][j]=-1; 50 FOR(i,1,n) res[i]=0; 51 } 52 53 bool find(int x,int n) { 54 lx[x]=1; 55 FOR(i,1,n) 56 if(!rx[i] && g[x][i]!=-1) { 57 int tmp=g[x][i]-l[x]-r[i]; 58 if(!tmp) { //相等子图 59 rx[i]=1; 60 if(!res[i] || find(res[i],n)) { 61 res[i]=x; 62 return 1; 63 } 64 } else { 65 slack[i]=min(slack[i],tmp); //更新 i 的 slack 66 } 67 } 68 return 0; 69 } 70 int solve(int n) { 71 if(!n) return 0; 72 FOR(i,1,n) r[i]=0; 73 FOR(i,1,n) { //初始化顶标 74 l[i]=INF; 75 FOR(j,1,n) if(g[i][j] != -1) { 76 l[i]=min(l[i],g[i][j]); 77 } 78 } 79 FOR(i,1,n) { //依次匹配 i 80 FOR(j,1,n) slack[j]=INF; 81 int cnt=0; 82 for(;;) { 83 cnt++; 84 FOR(j,1,n) lx[j]=rx[j]=0; 85 if(find(i,n)) break; 86 int mini=INF; //修改顶标使容纳更多的边 87 FOR(i,1,n) if(!rx[i]) //最小 slack S->T' 88 mini=min(mini,slack[i]); 89 FOR(i,1,n) { 90 if(lx[i]) l[i]+=mini; // S 和 T 集合的修改 保证原来的边依旧存在 91 if(rx[i]) r[i]-=mini; 92 else slack[i]-=mini; //改变 slack 93 } 94 } 95 } 96 int ans=0; 97 FOR(i,1,n) ans+=l[i]+r[i]; //相等子图的完美匹配 左右顶标之和 98 return ans; 99 } 100 } km; 101 102 void calcHash(int u) { 103 tmp.clear(); 104 FOR(i,0,(int)g[u].size()-1) { 105 int v=g[u][i].v; 106 if(v!=fa[u]&&!g[u][i].flag) 107 tmp.push_back(H[v]); 108 } 109 sort(tmp.begin(),tmp.end()); 110 H[u]=A; 111 FOR(i,0,(int)tmp.size()-1) 112 H[u]=(H[u]*P%MOD ^ tmp[i])%MOD; 113 H[u]=(H[u]*B) %MOD; 114 } 115 116 int bfs(int s) { 117 memset(vis,0,sizeof(vis)); 118 q.push(s); vis[s]=1; 119 int maxu=s; dis[s]=0; 120 while(!q.empty()) { 121 int u=q.front(); q.pop(); 122 FOR(i,0,(int)g[u].size()-1) { 123 int v=g[u][i].v; 124 if(!vis[v]) { 125 vis[v]=1; fa[v]=u; dis[v]=dis[u]+2; 126 if(dis[v]>dis[maxu]) maxu=v; 127 q.push(v); 128 } 129 } 130 } 131 return maxu; 132 } 133 void build(int u) { 134 FOR(i,0,(int)g[u].size()-1) { 135 int v=g[u][i].v; 136 if(v!=fa[u] && !g[u][i].flag) { 137 fa[v]=u; 138 dep[v]=dep[u]+1; 139 build(v); 140 } 141 } 142 calcHash(u); 143 } 144 int getroot() { 145 int x=bfs(1); 146 int y=bfs(x); 147 int mid=dis[y]/2,i; 148 for(i=y;i!=x;i=fa[i]) { 149 if(mid>=2) mid-=2; 150 else break; 151 } 152 if(!mid) { fa[i]=-1; build(i); return i; } 153 else { 154 ++n; 155 fa[n]=-1; 156 adde(n,i); adde(fa[i],n); 157 FOR(j,0,(int)g[i].size()-1) 158 if(g[i][j].v==fa[i]) g[i][j].flag=1; 159 FOR(j,0,(int)g[fa[i]].size()-1) 160 if(g[fa[i]][j].v==i) g[fa[i]][j].flag=1; 161 build(n); 162 return n; 163 } 164 } 165 166 bool cmp(const int& x,const int& y) { 167 return dep[x]>dep[y] || (dep[x]==dep[y]&&H[x]<H[y]); 168 } 169 170 int F[N][N],pos[N]; 171 172 void DP() { 173 memset(F,-1,sizeof(F)); 174 FOR(i,1,n) pos[i]=i; 175 sort(pos+1,pos+n+1,cmp); 176 FOR(st,1,n) { 177 int last=st; 178 while(last<=n&&dep[pos[last+1]]==dep[pos[st]]&&H[pos[last+1]]==H[pos[st]]) 179 last++; 180 FOR(i,st,last) FOR(j,st,last) { 181 int X=pos[i],Y=pos[j],tot=0; 182 FOR(k,0,(int)g[X].size()-1) 183 if(fa[X]!=g[X][k].v&&!g[X][k].flag) tot++; 184 km.clear(tot); 185 int idx=1,idy=1; 186 FOR(k,0,(int)g[X].size()-1) { 187 int v=g[X][k].v; 188 if(v!=fa[X] && !g[X][k].flag) { 189 idy=1; 190 FOR(k2,0,(int)g[Y].size()-1) { 191 int v2=g[Y][k2].v; 192 if(v2!=fa[Y] && !g[Y][k2].flag) { 193 km.g[idx][idy]=F[v][v2]; 194 idy++; 195 } 196 } 197 idx++; 198 } 199 } 200 F[X][Y]=km.solve(tot); 201 F[X][Y]+=firstST[X]==finalST[Y]? 0:1; 202 } 203 st=last; 204 } 205 } 206 207 int main() { 208 read(n); 209 int u,v; 210 FOR(i,1,n-1) { 211 read(u),read(v); 212 adde(u,v); 213 } 214 FOR(i,1,n) read(firstST[i]); 215 FOR(i,1,n) read(finalST[i]); 216 root=getroot(); 217 text(root,-1); 218 DP(); 219 printf("%d",F[root][root]); 220 return 0; 221 }
posted on 2016-03-03 10:03 hahalidaxin 阅读(485) 评论(0) 编辑 收藏 举报