bzoj 3197 [Sdoi2013]assassin(Hash+DP+KM)
Description
Input
Output
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 洛谷 题解 (除了代码均摘自喻队的博客,可是他退役了) 首先固定一棵树,枚举另一棵树,显然另一棵树只有与这棵树同构才有可能产生贡献 如果固定的树以重心为根,那么另一棵树最多就只有重心为 ...
随机推荐
- Does not contain a valid host:port authority: Master:8031 (configuration property 'yarn.resourcemanager.resource-tracker.address')
问题解决: 这个错误是:yarn里面的配置的格式有错误:如: <property> <name>yarn.resourcemanager.address</name> ...
- [转载]js 遍历数组对象
有一个JSON数组如下 all = {"error":0,"content":[{"name":"北京","v ...
- hdu 3778
简单的dfs 但繁琐的可以了 0.0 #include<cstdio> #include<cstring> #include<algorithm> using s ...
- hdu 1851 A Simple Game 博弈论
简单博弈问题(巴什博弈-Bash Game) 巴什博弈:只有一堆n个物品,两个人轮流从这对物品中取物,规定每次至少取一个,最多取m个,最后取光着得胜. 很容易想到当n%(m+1)!=0时,先取者必胜, ...
- 5.查找最小的k个元素(数组)
题目: 输入n个整数,输出其中最小的k个,例如输入1,2,3,4,5,6,7,8这8个数,则最小的4个是1,2,3,4(输出不要求有序) 解: 利用快速排序的partition,算导上求第k大数的思想 ...
- Spring MVC 与 web开发
转载:http://coderbee.net/index.php/java/20140719/959 项目组用了 Spring MVC 进行开发,觉得对里面的使用方式不是很满意,就想,如果是我来搭建开 ...
- 使用typeid(变量或类型).name()来获取常量或变量的类型---gyy整理
使用typeid(变量或类型).name()来获取常量或变量的类型 <typeinfo> 该头文件包含运行时类型识别(在执行时确定数据类型)的类 typeid的使用 typeid操作 ...
- 最受欢迎的5个Android ORM框架
在开发Android应用时,保存数据有这么几个方式, 一个是本地保存,一个是放在后台(提供API接口),还有一个是放在开放云服务上(如 SyncAdapter 会是一个不错的选择). 对于第一种方式, ...
- linux文件系统-基本磁盘2
直入主题-基本磁盘 硬盘数据按照不同特点和作用大致分为5部分:MBR区.DBR区.FAT区.DIR区和DATA区 1.MBR MBR(Main Boot Record 主引导记录区)位于整个硬盘的0磁 ...
- 50个非常有用的PHP工具
PHP是使用最为广泛的开源服务器端脚本语言之一,当然PHP并不是速度最快的,但它却是最常用的脚本语言.这里有50个有益的PHP工具,可以大大提高你的编程工作: 调试工具 Webgrind Xdebug ...