BZOJ 3197: [Sdoi2013]assassin 树形DP + 最小费用流 + 树的同构
Description
Input
Output
其实就是给出两颗树,求一种两种树同构的方式,使得不同颜色个数最少$.$
树的重新构建,其实就是指定不同的点为根节点$.$
好在树的重心有一个重要的性质:在一颗树上只有一个/两个点之间又一条边$.$
我们可以把第一棵树随便一个重心为根,求出每个点为根节点时的哈希值$.$
再枚举第二棵树的重心,如果这个重心为根的哈希值与第一个树根的哈希值相同,说明两个树以这两个点为根的形状是相同的,我们就可以进行DP$.$
令 $f[a][b]$ 表示 $a$ 为根与 $b$ 为根的子树进行匹配的最小代价(最少不同个数)$.$
当然,必须满足 $a$ 与 $b$ 的子树形态相同$.$
有一个问题:$a$ 的若干个儿子与 $b$ 的若干个儿子的哈希值都相同,那我们该怎么进行匹配呢?因为显然,只能是两两一一配对$.$
先求出 $a$ 的所有儿子与 $b$ 的所有儿子匹配的最小代价 $f[son[a]][son[b]]$.
发现这其实是一个二分图模型,即二分图最小匹配.
将儿子哈希值相同的连边,跑一个最小费用流就能帮助我们决策出哪两个匹配是最优的$.$
这么递归下去即可$.$
感觉好多题都是这种套路:很难通过人脑进行决策,那就直接让某些特定的算法(如网络流,最小生成树)来帮我们进行一个决策$.$
// luogu-judger-enable-o2
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#define ll long long
#define inf 1000
#define setIO(s) freopen(s".in", "r" , stdin)
using namespace std;
namespace MCMF
{
#define maxn 40
struct Edge
{
int from,to,cap,cost;
Edge(int a=0,int b=0,int c=0,int d=0):from(a),to(b),cap(c),cost(d){}
};
queue<int>Q;
vector<Edge>edges;
vector<int>G[maxn];
int d[maxn],flow2[maxn],inq[maxn],pre[maxn],s,t,ans;
inline void addedge(int u,int v,int c,int d)
{
edges.push_back(Edge(u,v,c,d)), edges.push_back(Edge(v,u,0,-d));
G[u].push_back(edges.size()-2), G[v].push_back(edges.size()-1);
}
inline int spfa()
{
int i,j;
memset(inq,0,sizeof(inq));
for(i=0;i<maxn;++i) d[i]=flow2[i]=inf;
Q.push(s),d[s]=0,inq[s]=1;
while(!Q.empty())
{
int u=Q.front(); Q.pop(), inq[u]=0;
for(i=0;i<G[u].size();++i)
{
Edge e=edges[G[u][i]];
if(d[e.to]>d[u]+e.cost&&e.cap)
{
d[e.to]=d[u]+e.cost, pre[e.to]=G[u][i];
flow2[e.to]=min(e.cap,flow2[u]);
if(!inq[e.to])
{
inq[e.to]=1;
Q.push(e.to);
}
}
}
}
int f=flow2[t],tr=t;
if(f==inf) return 0;
edges[pre[tr]].cap-=f, edges[pre[tr]^1].cap+=f, tr=edges[pre[tr]].from;
while(tr!=s)
edges[pre[tr]].cap-=f,edges[pre[tr]^1].cap+=f,tr=edges[pre[tr]].from;
ans+=d[t]*f;
return 1;
}
inline void re()
{
edges.clear();
memset(pre,0,sizeof(pre));
for(int i=0;i<maxn;++i) G[i].clear();
ans=0;
}
inline int getcost()
{
while(spfa());
return ans;
}
};
const int ha=2019, mul=5589, mod=233233, N=802;
int n, edges,root;
int hd[N],nex[N<<1],to[N<<1],s1[N],s2[N],f[N][N];
inline void addedge(int u,int v)
{
nex[++edges]=hd[u],hd[u]=edges,to[edges]=v;
}
struct G
{
vector<int>sor[N];
int Hash[N], siz[N], mx[N],root;
void getroot(int u,int ff)
{
siz[u]=1,mx[u]=0;
for(int i=hd[u];i;i=nex[i])
if(to[i]!=ff)
getroot(to[i],u), siz[u]+=siz[to[i]],mx[u]=max(mx[u],siz[to[i]]);
mx[u]=max(mx[u],n-siz[u]);
if(mx[u]<mx[root]) root=u;
}
void calc(int u,int ff)
{
sor[u].clear(), Hash[u]=ha;
for(int i=hd[u];i;i=nex[i])
if(to[i]!=ff)
calc(to[i],u), sor[u].push_back(Hash[to[i]]);
sort(sor[u].begin(),sor[u].end());
for(int i=0;i<sor[u].size();++i) Hash[u]=((Hash[u]*mul)^sor[u][i])%mod;
}
}t[6];
vector<int>c1[N],c2[N];
int solve(int x,int fx,int y,int fy,int ty)
{
if(f[x][y]!=-1) return f[x][y];
f[x][y]=s1[x]^s2[y];
int i,j,nn=0;
for(i=hd[x];i;i=nex[i])
{
if(to[i]==fx) continue;
for(j=hd[y];j;j=nex[j])
{
if(to[j]==fy) continue;
if(t[0].Hash[to[i]]==t[ty].Hash[to[j]])
solve(to[i],x,to[j],y,ty);
}
}
c1[x].clear(),c2[x].clear();
for(i=hd[x];i;i=nex[i]) if(to[i]!=fx) ++nn, c1[x].push_back(to[i]);
for(i=hd[y];i;i=nex[i]) if(to[i]!=fy) c2[x].push_back(to[i]);
MCMF::re();
for(i=0;i<c1[x].size();++i)
for(j=0;j<c2[x].size();++j)
if(t[0].Hash[c1[x][i]]==t[ty].Hash[c2[x][j]])
MCMF::addedge(i+1,j+1+nn,1,f[c1[x][i]][c2[x][j]]);
MCMF::s=0,MCMF::t=nn+c2[x].size()+1;
for(i=1;i<=nn;++i) MCMF::addedge(0,i,1,0);
for(i=1;i<=c2[x].size();++i) MCMF::addedge(nn+i,nn+c2[x].size()+1,1,0);
f[x][y]+=MCMF::getcost();
return f[x][y];
}
int main()
{
int i,j,ans,ty=0;
// setIO("input");
scanf("%d", &n);
for(i=1;i<n;++i)
{
int a, b;
scanf("%d%d",&a,&b),addedge(a,b),addedge(b,a);
}
for(i=1;i<=n;++i) scanf("%d",&s1[i]);
for(i=1;i<=n;++i) scanf("%d",&s2[i]);
t[0].mx[0]=n, t[0].getroot(1,0), t[0].calc(t[0].root,0);
for(ans=n,i=1;i<=n;++i)
if(t[0].mx[i]==t[0].mx[t[0].root])
{
++ty;
t[ty].calc(i, 0);
if(t[ty].Hash[i]==t[0].Hash[t[0].root])
{
memset(f,-1,sizeof(f));
ans=min(ans,solve(t[0].root,0,i,0,ty));
}
}
printf("%d\n",ans);
return 0 ;
}
BZOJ 3197: [Sdoi2013]assassin 树形DP + 最小费用流 + 树的同构的更多相关文章
- 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,D ...
- BZOJ 3197 [Sdoi2013]assassin
题解: 树上Hash 首先重心在边上就把边分裂 以重心为根建树,这样两个根一定对应 然后f[i][j]表示i匹配另一棵的j节点的最小代价 把他们的儿子摘出来做最小权匹配即可 #include<i ...
- 【BZOJ-1040】骑士 树形DP + 环套树 + DFS
1040: [ZJOI2008]骑士 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 3312 Solved: 1269[Submit][Status ...
- HDU 4514 - 湫湫系列故事——设计风景线 - [并查集判无向图环][树形DP求树的直径]
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4514 Time Limit: 6000/3000 MS (Java/Others) Memory Li ...
- 浅谈关于树形dp求树的直径问题
在一个有n个节点,n-1条无向边的无向图中,求图中最远两个节点的距离,那么将这个图看做一棵无根树,要求的即是树的直径. 求树的直径主要有两种方法:树形dp和两次bfs/dfs,因为我太菜了不会写后者这 ...
- Bzoj 1131[POI2008]STA-Station (树形DP)
Bzoj 1131[POI2008]STA-Station (树形DP) 状态: 设\(f[i]\)为以\(i\)为根的深度之和,然后考虑从他父亲转移. 发现儿子的深度及其自己的深度\(-1\) 其余 ...
- 树形DP 学习笔记(树形DP、树的直径、树的重心)
前言:寒假讲过树形DP,这次再复习一下. -------------- 基本的树形DP 实现形式 树形DP的主要实现形式是$dfs$.这是因为树的特殊结构决定的——只有确定了儿子,才能决定父亲.划分阶 ...
- BZOJ 1040 树形DP+环套树
就是有n个点n条边,那么有且只有一个环那么用Dfs把在环上的两个点找到.然后拆开,从这条个点分别作树形Dp即可. #include <cstdio> #include <cstrin ...
- 算法笔记--树的直径 && 树形dp && 虚树 && 树分治 && 树上差分 && 树链剖分
树的直径: 利用了树的直径的一个性质:距某个点最远的叶子节点一定是树的某一条直径的端点. 先从任意一顶点a出发,bfs找到离它最远的一个叶子顶点b,然后再从b出发bfs找到离b最远的顶点c,那么b和c ...
随机推荐
- hue数据导出
1/ 执行要下载的sql语句 2/ 执行完成后,点击下面导出 3.选择所有 4.选择每个人对应的文件夹,没有自己名称的可以创建一个 5.选择导出 6.右面是导出执行时的界面 7.选择已经执行的文件 ...
- 小记---------网页采集之Jsoup
Jsoup是一款Java解析器,相当于httpClient解析器 功能:①:从一个URL,文件或字符串中解析HTML ②:使用DOM或CSS选择器来查找.取出数据 ...
- Luogu P3195 [HNOI2008]玩具装箱
题目 预处理\(C\)的前缀和\(sum\).设前\(i\)个物品的最小答案为\(f\). \(f_i=\max\limits_{j\in[1,i)}(f_j+(sum_i-sum_j-L)^2)\) ...
- git部分命令笔记
目录 配置user信息 建Git仓库 清空暂存区 git变更文件名 查看暂存区状态 查看历史 查看本地分支 查看所有分支(包含远程) 创建分支 基于远程分支创建本地新分支 查看图形化分支日志 图形化界 ...
- reload() 方法用于重新加载当前文档。配合Ajax异步请求。
1. reload() 方法, reload() 方法用于重新加载当前文档.配合Ajax异步请求. http://www.w3school.com.cn/jsref/met_loc_reload.as ...
- 解决PHP上传文件、下载文件中由于文件过大导致的上传失败及下载不全问题
用php+apache上传文件的时候,由于文件过大,容易导致上传失败, 解决办法:修改php.ini中:upload_max_filesize 2m 即允许上传文件大小的最大值.默认为2M ,大小 ...
- Git-版本控制 (三)
前面两篇我们成功安装了Git,并且成功创建了我们的版本库. 创建了版本库,怎么可以不往里面放内容呢,所以今天的任务就是学会将文件放入至我们的版本库中...... 首先,我们点开我们的"Git ...
- 项目常用的几个mysql函数
1.find_in_set函数 find_in_set(str,strlist); str是一个字符串 strlist是字符串列表--一个有多个子链被“,”分开的字符串 有多种情况: a.str为nu ...
- 剑指offer-4:变态条楼梯
##四.变态条楼梯 ###题目描述 一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级.求该青蛙跳上一个n级的台阶总共有多少种跳法. ###分析 也是斐波那契数列问题,根据上述的思路,可 ...
- ssh远程登录故障解决方案
问题描述: xshell远程连接服务器连接不上,如下图所示: 故障排除: . 首先查看自己系统的防火墙是否关闭,没有关闭的话关闭一下. # centos 7中关闭防火墙命令: systemctl st ...