bzoj 3197 [Sdoi2013]assassin(Hash+DP+KM)
Description
.jpg)
Input
.jpg)
Output
.jpg)
Sample Input
1 2
2 3
3 4
0 0 1 1
1 0 0 0
Sample Output
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]。
【代码】
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define FOR(a,b,c) for(int a=(b);a<=(c);a++)
using namespace std; typedef long long LL;
const int N = ;
const int INF = 1e9;
const int A = ;
const int B = ;
const int P = ;
const int MOD = ; struct Edge { int v,flag;
};
void read(int& x) {
char c=getchar(); int f=; x=;
while(!isdigit(c)) {if(c=='-')f=-; c=getchar();}
while(isdigit(c)) x=x*+c-'',c=getchar();
x*=f;
} vector<Edge> g[N];
vector<int> tmp;
queue<int> q; void adde(int u,int v) {
g[u].push_back((Edge){v,});
g[v].push_back((Edge){u,});
} int n,root;
int dis[N],dep[N],fa[N],vis[N];
int firstST[N],finalST[N];
LL H[N]; struct KM {
int g[N][N];
int lx[N],rx[N];
int l[N],r[N],res[N];
int slack[N]; void clear(int n) {
FOR(i,,n) FOR(j,,n) g[i][j]=-;
FOR(i,,n) res[i]=;
} bool find(int x,int n) {
lx[x]=;
FOR(i,,n)
if(!rx[i] && g[x][i]!=-) {
int tmp=g[x][i]-l[x]-r[i];
if(!tmp) { //相等子图
rx[i]=;
if(!res[i] || find(res[i],n)) {
res[i]=x;
return ;
}
} else {
slack[i]=min(slack[i],tmp); //更新 i 的 slack
}
}
return ;
}
int solve(int n) {
if(!n) return ;
FOR(i,,n) r[i]=;
FOR(i,,n) { //初始化顶标
l[i]=INF;
FOR(j,,n) if(g[i][j] != -) {
l[i]=min(l[i],g[i][j]);
}
}
FOR(i,,n) { //依次匹配 i
FOR(j,,n) slack[j]=INF;
int cnt=;
for(;;) {
cnt++;
FOR(j,,n) lx[j]=rx[j]=;
if(find(i,n)) break;
int mini=INF; //修改顶标使容纳更多的边
FOR(i,,n) if(!rx[i]) //最小 slack S->T'
mini=min(mini,slack[i]);
FOR(i,,n) {
if(lx[i]) l[i]+=mini; // S 和 T 集合的修改 保证原来的边依旧存在
if(rx[i]) r[i]-=mini;
else slack[i]-=mini; //改变 slack
}
}
}
int ans=;
FOR(i,,n) ans+=l[i]+r[i]; //相等子图的完美匹配 左右顶标之和
return ans;
}
} km; void calcHash(int u) {
tmp.clear();
FOR(i,,(int)g[u].size()-) {
int v=g[u][i].v;
if(v!=fa[u]&&!g[u][i].flag)
tmp.push_back(H[v]);
}
sort(tmp.begin(),tmp.end());
H[u]=A;
FOR(i,,(int)tmp.size()-)
H[u]=(H[u]*P%MOD ^ tmp[i])%MOD;
H[u]=(H[u]*B) %MOD;
} int bfs(int s) {
memset(vis,,sizeof(vis));
q.push(s); vis[s]=;
int maxu=s; dis[s]=;
while(!q.empty()) {
int u=q.front(); q.pop();
FOR(i,,(int)g[u].size()-) {
int v=g[u][i].v;
if(!vis[v]) {
vis[v]=; fa[v]=u; dis[v]=dis[u]+;
if(dis[v]>dis[maxu]) maxu=v;
q.push(v);
}
}
}
return maxu;
}
void build(int u) {
FOR(i,,(int)g[u].size()-) {
int v=g[u][i].v;
if(v!=fa[u] && !g[u][i].flag) {
fa[v]=u;
dep[v]=dep[u]+;
build(v);
}
}
calcHash(u);
}
int getroot() {
int x=bfs();
int y=bfs(x);
int mid=dis[y]/,i;
for(i=y;i!=x;i=fa[i]) {
if(mid>=) mid-=;
else break;
}
if(!mid) { fa[i]=-; build(i); return i; }
else {
++n;
fa[n]=-;
adde(n,i); adde(fa[i],n);
FOR(j,,(int)g[i].size()-)
if(g[i][j].v==fa[i]) g[i][j].flag=;
FOR(j,,(int)g[fa[i]].size()-)
if(g[fa[i]][j].v==i) g[fa[i]][j].flag=;
build(n);
return n;
}
} bool cmp(const int& x,const int& y) {
return dep[x]>dep[y] || (dep[x]==dep[y]&&H[x]<H[y]);
} int F[N][N],pos[N]; void DP() {
memset(F,-,sizeof(F));
FOR(i,,n) pos[i]=i;
sort(pos+,pos+n+,cmp);
FOR(st,,n) {
int last=st;
while(last<=n&&dep[pos[last+]]==dep[pos[st]]&&H[pos[last+]]==H[pos[st]])
last++;
FOR(i,st,last) FOR(j,st,last) {
int X=pos[i],Y=pos[j],tot=;
FOR(k,,(int)g[X].size()-)
if(fa[X]!=g[X][k].v&&!g[X][k].flag) tot++;
km.clear(tot);
int idx=,idy=;
FOR(k,,(int)g[X].size()-) {
int v=g[X][k].v;
if(v!=fa[X] && !g[X][k].flag) {
idy=;
FOR(k2,,(int)g[Y].size()-) {
int v2=g[Y][k2].v;
if(v2!=fa[Y] && !g[Y][k2].flag) {
km.g[idx][idy]=F[v][v2];
idy++;
}
}
idx++;
}
}
F[X][Y]=km.solve(tot);
F[X][Y]+=firstST[X]==finalST[Y]? :;
}
st=last;
}
} int main() {
read(n);
int u,v;
FOR(i,,n-) {
read(u),read(v);
adde(u,v);
}
FOR(i,,n) read(firstST[i]);
FOR(i,,n) read(finalST[i]);
root=getroot();
text(root,-);
DP();
printf("%d",F[root][root]);
return ;
}
bzoj 3197 [Sdoi2013]assassin(Hash+DP+KM)的更多相关文章
- bzoj 3131 [Sdoi2013]淘金(数位DP+优先队列)
Description 小Z在玩一个叫做<淘金者>的游戏.游戏的世界是一个二维坐标.X轴.Y轴坐标范围均为1..N.初始的时候,所有的整数坐标点上均有一块金子,共N*N块. 一阵风吹 ...
- bzoj 3131 [Sdoi2013]淘金(数位dp)
题目描述 小Z在玩一个叫做<淘金者>的游戏.游戏的世界是一个二维坐标.X轴.Y轴坐标范围均为1..N.初始的时候,所有的整数坐标点上均有一块金子,共N*N块. 一阵风吹过,金子的位置发生了 ...
- sdoi2013 spring(hash+容斥)
大体思路是先求出来\(f[i]\)代表有至少\(i\)个位置相同的点对数. 然后就已经没什么好害怕的了(跟BZOJ3622一样) 然后这个\(f[i\)]怎么求呢? 最无脑的方法就是枚举位置,然后\( ...
- BZOJ 3197: [Sdoi2013]assassin 树形DP + 最小费用流 + 树的同构
Description Input Output 其实就是给出两颗树,求一种两种树同构的方式,使得不同颜色个数最少$.$树的重新构建,其实就是指定不同的点为根节点$.$ 好在树的重心有一个重要的性质: ...
- BZOJ 1090 字符串折叠(Hash + DP)
题目链接 字符串折叠 区间DP.$f[l][r]$为字符串在区间l到r的最小值 正常情况下 $f[l][r] = min(f[l][r], f[l][l+k-1]+f[l+k][r]);$ 当$l$到 ...
- BZOJ 3197 [Sdoi2013]assassin
题解: 树上Hash 首先重心在边上就把边分裂 以重心为根建树,这样两个根一定对应 然后f[i][j]表示i匹配另一棵的j节点的最小代价 把他们的儿子摘出来做最小权匹配即可 #include<i ...
- bzoj 3123: [Sdoi2013]森林(45分暴力)
3123: [Sdoi2013]森林 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 4184 Solved: 1235[Submit][Status ...
- 2021.08.03 BZOJ 疯狂的馒头(并查集)
2021.08.03 BZOJ 疯狂的馒头(并查集) 疯狂的馒头 - 题目 - 黑暗爆炸OJ (darkbzoj.tk) 重点: 1.并查集的神奇运用 2.离线化 题意: 给一个长为n的序列,进行m次 ...
- Bzoj3197/洛谷3296 [SDOI2013]刺客信条assassin(树的重心+树Hash+树形DP+KM)
题面 Bzoj 洛谷 题解 (除了代码均摘自喻队的博客,可是他退役了) 首先固定一棵树,枚举另一棵树,显然另一棵树只有与这棵树同构才有可能产生贡献 如果固定的树以重心为根,那么另一棵树最多就只有重心为 ...
随机推荐
- dictionary 和 hashtable 区别
区别:1,Dictionary支持泛型,而Hashtable不支持. 2,Dictionary没有装填因子(Load Facto)概念,当容量不够时才扩容(扩容跟Hashtable一样,也是两倍于当前 ...
- hdu 4665
转载一下 用的搜索 竟然过了 ............代码 ....... #include<stdio.h> #include<string.h> #include&l ...
- hdu 1760 A New Tetris Game 博弈论
找sg值,可以选择暴力,也可以利用sg值的特点简化. 暴力就跟取石子一样,没什么差别,DFS搞定.把矩阵看成一个字符串,字符串就是一个状态. 其实我们也可以不暴力求sg值,因为只要当前状态能到达一个s ...
- 用java运行Hadoop程序报错:org.apache.hadoop.fs.LocalFileSystem cannot be cast to org.apache.
用java运行Hadoop例程报错:org.apache.hadoop.fs.LocalFileSystem cannot be cast to org.apache.所写代码如下: package ...
- ArcGIS学习记录—Arcgis中点、线、面的相互转换方法
本文使用的工具在Arctoolbox.Data Management Tools.Features (一)面--面转线.面转点 面转线 Polygon To Line .Feature To Lin ...
- vimrc for windows
set nobackupsource $VIMRUNTIME/vimrc_example.vimsource $VIMRUNTIME/mswin.vimbehave mswin:color deser ...
- *J2EE中乱码处理
发生中文乱码有三种情况 表单form (1)post 首先确定浏览器的编码方式,比如说utf-8,请求发给web服务器,web服务器以编码方式iso-9959-1来接收数据(服务器是外国人编写的),服 ...
- linux下关于程序性能和系统性能的工具、方法
观察性能/状态的方法:top free netstat /pro/目录下的信息 其中/pro/meminfo下的信息相当丰富 ------------------------------------- ...
- 捉虫记2:windows程序句柄泄露的上下文环境
作为程序员,开发程序是基本功,而调试程序也是必不可少的技能之一.软件在主体功能开发完成后会经历各个阶段的测试,才会被发布.在测试过程中,出现较多的可能就是内存泄漏,句柄泄漏,异常崩溃等属于非功能型的软 ...
- 【HDOJ】2385 Stock
水题,逆向做+优先级队列. /* 2385 */ #include <iostream> #include <sstream> #include <string> ...