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 洛谷 题解 (除了代码均摘自喻队的博客,可是他退役了) 首先固定一棵树,枚举另一棵树,显然另一棵树只有与这棵树同构才有可能产生贡献 如果固定的树以重心为根,那么另一棵树最多就只有重心为 ...
随机推荐
- [转载]C#对象序列化与反序列化
文章写的实在是太好了,忍不住转来: http://www.cnblogs.com/LiZhiW/p/3622365.html#_Toc8478 1.对象序列化的介绍 (1).NET支持对象序列化的几种 ...
- Akka学习——术语和概念
(大部分为翻译) Concurrency vs. Parallelism 并发 vs 并行 并发并不一定同时运行,比如使用时间片,使得两个任务交替执行.而并行是执两个任务真正的同时执行. ...
- 从零开始运维之旅:如何监控你的 Windows?
小弟乃刚刚踏入运维圈的资深小白一枚,正所谓完事开头难,公司里怕我把生产系统搞坏就让我先在测试环境上先练练手.巧的是测试环境又是我熟悉的 Windows 环境,心中窃喜啊.但问题随之而来,运维从何下手呢 ...
- Ubuntu环境下手动配置Hadoop1.2.1
/×××××××××××××××××××××××××××××××××××××××××/ Author:xxx0624 HomePage:http://www.cnblogs.com/xxx0624/ ...
- BZOJ 3925 ZJOI2015 地震后的幻想乡
假设我们用了边权前i小的边使得图连通,那么对答案的贡献为i/m+1 又因为期望的线性性质,我们只需要求用了i条边就可以了 不妨设g(S)(i)表示用了i条边使得点集S连通的概率 设f(S)(i)表示用 ...
- 解决浮层弹出如何加上datepicker,并且浮动在上面
最近在做一个弹出层上弹出的对话框中能弹出一个截止时间的选择框,这个选择框使用datepicker来做. 效果大致是这样的: 但是在做的时候,遇到一个问题,datepicker在弹出层的时候,时间选择框 ...
- 每个QWidget都有contentsMargins函数,善用QMargins
m_pSearchLineEdit = new QLineEdit(); QPushButton *pSearchButton = new QPushButton(this); pSearchButt ...
- MyBatis的动态SQL操作--查询
查询条件不确定,需要根据情况产生SQL语法,这种情况叫动态SQL,即根据不同的情况生成不同的sql语句. 模拟一个场景,在做多条件搜索的时候,
- java文档注释主要使用方法
一.java包含哪些注释 1.//用于单行注释. 2./*...*/用于多行注释,从/*开始,到*/结束,不能嵌套. 3./**...*/则是为支持jdk工具javadoc.exe而特有的注释语句.这 ...
- Git教程(4)常用小技巧之标签
Git 使用两种主要类型的标签:轻量标签(lightweight)与附注标签(annotated). 一个轻量标签很像一个不会改变的分支 - 它只是一个特定提交的引用. 然而,附注标签是存储在 Git ...