山东济南彤昌机械科技有限公司 山东济南江鹏工贸游有限公司

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

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  阅读(472)  评论(0编辑  收藏  举报