[JZOJ3296] 【SDOI2013】刺客信条
题目
题目大意
给你一棵树,树上每个节点有000或111的状态。
用最少的操作次数使得当前状态与目标状态同构。
思考历程
首先想到的是找重心。
因为根是不确定的,但重心只会有一个或两个,以重心为根就能方便很多。
如果重心有两个,就将连接它们的边拆成点,让它们分别与这个点相连就好了(重心是连在一起的)。
然后就是树上哈希……
哈希之后就开始了艰辛的思考历程……
于是比赛就结束了。
正解
我前面的思考是没有问题的。
后面的该怎么处理?当然是DP啊!
设fi,jf_{i,j}fi,j表示子树iii对应子树jjj的最小操作数。
显然如果iii能匹配jjj,就必须要保证它们的哈希值相等。
但是还会有其它条件,综合起来就是它们祖先的哈希值也相等……
其实为了简化操作,我们再保证它们的深度相等就行了,虽然这并不能保证它们真的能够匹配,但也就算了吧……(我曾试过将完全出去这些冗余状态,但程序跑得更慢了,这意味着数据的这一类冗余状态并不多)
所以可以将点按照深度和哈希值排序,从后往前做。
接下来考虑转移,首先ai xor bja_i \ xor \ b_jai xor bj是一定要加上的、
然后就将各自的子树两两配对,也就是∑fx,y\sum f_{x,y}∑fx,y,其中xxx是iii的儿子,yyy是jjj的儿子,而且xxx和yyy哈希值相等。
我们要保证这个东西最小。
于是这就变成了二分图的最小权完备匹配。
题目说每个点的度数小于等于111111,这意味着看起来能够状压DP。
但实际上,如果不非常用力地开常数,那状压DP是很难卡过去的……(我打了80分)。
于是就可以用费用流或KM算法(因为这题求的是最小权,所以将边权取相反数就可以了)。
时间复杂度为玄学……
代码
状压DP
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 710
#define mod 1000000007
inline void get_min(int &a,int b){
a>b?a=b:0;
}
int n;
int e[N][11],ne[N];
int bz[N][11],nbz;
int siz[N];
int tmpr[2],cnt,root;
void get_siz(int x,int fa){
bool is_root=1;
siz[x]=1;
for (int i=0;i<ne[x];++i){
int y=e[x][i];
if (y!=fa){
get_siz(y,x);
siz[x]+=siz[y];
if (y!=fa)
is_root&=(siz[y]<<1<=n);
}
}
is_root&=(n-siz[x]<<1<=n);
if (is_root)
tmpr[cnt++]=x;
}
int fa[N],dep[N];
long long powd[N],h[N];
inline bool cmpe(int x,int y){
return siz[x]<siz[y] || siz[x]==siz[y] && h[x]<h[y];
}
void get_hash(int x){
dep[x]=dep[fa[x]]+1;
siz[x]=1;
for (int i=0;i<ne[x];++i){
int y=e[x][i];
if (y!=fa[x]){
fa[y]=x;
get_hash(y);
siz[x]+=siz[y];
}
else{
for (int j=i;j<ne[x]-1;++j)
e[x][j]=e[x][j+1];
ne[x]--;
--i;
}
}
sort(e[x],e[x]+ne[x],cmpe);
h[x]=siz[x];
int w=1;
for (int i=0;i<ne[x];++i){
int y=e[x][i];
h[x]=(h[x]+h[y]*powd[w])%mod;
w+=siz[y];
}
}
int q[N];
inline bool cmpq(int x,int y){
return dep[x]<dep[y] || dep[x]==dep[y] && h[x]<h[y];
}
int f[N][N],g[13][2048];
//int bg[13][13];
int a[N],b[N];
int main(){
freopen("in.txt","r",stdin);
scanf("%d",&n);
for (int i=1;i<n;++i){
int u,v;
scanf("%d%d",&u,&v);
e[u][ne[u]++]=v;
e[v][ne[v]++]=u;
}
for (int i=1;i<=n;++i)
scanf("%d",&a[i]);
for (int i=1;i<=n;++i)
scanf("%d",&b[i]);
get_siz(1,0);
if (cnt==2){
int u=tmpr[0],v=tmpr[1];
root=++n;
for (int i=0;i<ne[u];++i)
if (e[u][i]==v){
e[u][i]=root;
break;
}
for (int i=0;i<ne[v];++i)
if (e[v][i]==u){
e[v][i]=root;
break;
}
e[root][0]=u,e[root][1]=v,ne[root]=2;
}
else
root=tmpr[0];
powd[0]=1;
for (int i=1;i<=n;++i)
powd[i]=powd[i-1]*997%mod;
get_hash(root);
for (int i=1;i<=n;++i)
q[i]=i;
sort(q+1,q+n+1,cmpq);
memset(f,63,sizeof f);
for (int i=n,r=n,ii=q[i];i>=1;ii=q[--i]){
for (int j=i,jj=q[j];j>=1 && dep[ii]==dep[jj] && h[ii]==h[jj];jj=q[--j]){
memset(g,63,sizeof g);
g[0][0]=0;
for (int x=0;x<ne[ii];++x){
int xx=e[ii][x];
for (int k=0;k<1<<ne[ii];++k)
for (int y=0;y<ne[jj];++y){
if (k>>y&1)
continue;
get_min(g[x+1][k|1<<y],g[x][k]+f[xx][e[jj][y]]);
}
}
f[ii][jj]=g[ne[ii]][(1<<ne[ii])-1]+(a[ii]^b[jj]);
if (ii==jj)
continue;
memset(g,63,sizeof g);
g[0][0]=0;
for (int y=0;y<ne[jj];++y){
int yy=e[jj][y];
for (int k=0;k<1<<ne[jj];++k)
for (int x=0;x<ne[ii];++x){
if (k>>x&1)
continue;
get_min(g[y+1][k|1<<x],g[y][k]+f[yy][e[ii][x]]);
}
}
f[jj][ii]=g[ne[jj]][(1<<ne[jj])-1]+(a[jj]^b[ii]);
}
}
printf("%d\n",f[root][root]);
return 0;
}
KM算法
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 710
#define mod 1000000007
inline void get_min(int &a,int b){
a>b?a=b:0;
}
int n;
int e[N][11],ne[N];
int bz[N][11],nbz;
int siz[N];
int tmpr[2],cnt,root;
void get_siz(int x,int fa){
bool is_root=1;
siz[x]=1;
for (int i=0;i<ne[x];++i){
int y=e[x][i];
if (y!=fa){
get_siz(y,x);
siz[x]+=siz[y];
if (y!=fa)
is_root&=(siz[y]<<1<=n);
}
}
is_root&=(n-siz[x]<<1<=n);
if (is_root)
tmpr[cnt++]=x;
}
int fa[N],dep[N];
long long powd[N],h[N];
inline bool cmpe(int x,int y){
return siz[x]<siz[y] || siz[x]==siz[y] && h[x]<h[y];
}
void get_hash(int x){
dep[x]=dep[fa[x]]+1;
siz[x]=1;
for (int i=0;i<ne[x];++i){
int y=e[x][i];
if (y!=fa[x]){
fa[y]=x;
get_hash(y);
siz[x]+=siz[y];
}
else{
for (int j=i;j<ne[x]-1;++j)
e[x][j]=e[x][j+1];
ne[x]--;
--i;
}
}
sort(e[x],e[x]+ne[x],cmpe);
h[x]=siz[x];
int w=1;
for (int i=0;i<ne[x];++i){
int y=e[x][i];
h[x]=(h[x]+h[y]*powd[w])%mod;
w+=siz[y];
}
}
int q[N];
inline bool cmpq(int x,int y){
return dep[x]<dep[y] || dep[x]==dep[y] && h[x]<h[y];
}
int f[N][N];
int m;
int bg[13][13];
int exl[13],exr[13];
bool visl[13],visr[13];
int bel[13];
bool find(int x){
visl[x]=1;
for (int i=0;i<m;++i)
if (exl[x]+exr[i]==bg[x][i] && !visr[i]){
visr[i]=1;
if (bel[i]==-1 || find(bel[i])){
bel[i]=x;
return 1;
}
}
return 0;
}
inline int km(){
memset(bel,255,sizeof bel);
for (int i=0;i<m;++i){
exl[i]=bg[i][0];
for (int j=1;j<m;++j)
exl[i]=max(exl[i],bg[i][j]);
}
memset(exr,0,sizeof exr);
for (int k=0;k<m;++k){
while (1){
memset(visl,0,sizeof visl);
memset(visr,0,sizeof visr);
if (find(k))
break;
int d=0x3f3f3f3f;
for (int i=0;i<m;++i)
if (visl[i])
for (int j=0;j<m;++j)
if (!visr[j])
d=min(d,exl[i]+exr[j]-bg[i][j]);
for (int i=0;i<m;++i)
if (visl[i])
exl[i]-=d;
for (int j=0;j<m;++j)
if (visr[j])
exr[j]+=d;
}
}
int res=0;
for (int i=0;i<m;++i)
res+=bg[bel[i]][i];
return -res;
}
int a[N],b[N];
inline void calc(int ii,int jj){
for (int x=0;x<ne[ii];++x){
int xx=e[ii][x];
for (int y=0;y<ne[jj];++y)
bg[x][y]=-f[xx][e[jj][y]];
}
m=ne[ii];
f[ii][jj]=km()+(a[ii]^b[jj]);
}
int main(){
freopen("in.txt","r",stdin);
scanf("%d",&n);
for (int i=1;i<n;++i){
int u,v;
scanf("%d%d",&u,&v);
e[u][ne[u]++]=v;
e[v][ne[v]++]=u;
}
for (int i=1;i<=n;++i)
scanf("%d",&a[i]);
for (int i=1;i<=n;++i)
scanf("%d",&b[i]);
get_siz(1,0);
if (cnt==2){
int u=tmpr[0],v=tmpr[1];
root=++n;
for (int i=0;i<ne[u];++i)
if (e[u][i]==v){
e[u][i]=root;
break;
}
for (int i=0;i<ne[v];++i)
if (e[v][i]==u){
e[v][i]=root;
break;
}
e[root][0]=u,e[root][1]=v,ne[root]=2;
}
else
root=tmpr[0];
powd[0]=1;
for (int i=1;i<=n;++i)
powd[i]=powd[i-1]*997%mod;
get_hash(root);
for (int i=1;i<=n;++i)
q[i]=i;
sort(q+1,q+n+1,cmpq);
memset(f,63,sizeof f);
for (int i=n,r=n,ii=q[i];i>=1;ii=q[--i]){
for (int j=i,jj=q[j];j>=1 && dep[ii]==dep[jj] && h[ii]==h[jj];jj=q[--j]){
calc(ii,jj);
if (ii!=jj)
calc(jj,ii);
}
}
printf("%d\n",f[root][root]);
return 0;
}
总结
想不出来的东西就用网络流吧……
[JZOJ3296] 【SDOI2013】刺客信条的更多相关文章
- 【BZOJ3197】[SDOI2013]刺客信条
[BZOJ3197][SDOI2013]刺客信条 题面 bzoj 洛谷 题解 关于树的同构,有一个非常好的性质: 把树的重心抠出来,那么会出现两种情况: 1.有一个重心,那么我们直接把这个重心作为树的 ...
- [BZOJ3197][SDOI2013]刺客信条assassin
bzoj luogu Description 故事发生在1486 年的意大利,Ezio原本只是一个文艺复兴时期的贵族,后来因为家族成员受到圣殿骑士的杀害,决心成为一名刺客.最终,凭借着他的努力和出众的 ...
- BZOJ3197:[SDOI2013]刺客信条——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=3197 故事发生在1486 年的意大利,Ezio 原本只是一个文艺复兴时期的贵族,后来因为家族成员受 ...
- Bzoj3197/洛谷3296 [SDOI2013]刺客信条assassin(树的重心+树Hash+树形DP+KM)
题面 Bzoj 洛谷 题解 (除了代码均摘自喻队的博客,可是他退役了) 首先固定一棵树,枚举另一棵树,显然另一棵树只有与这棵树同构才有可能产生贡献 如果固定的树以重心为根,那么另一棵树最多就只有重心为 ...
- [SDOI2013]刺客信条
Description 故事发生在1486 年的意大利,Ezio 原本只是一个文艺复兴时期的贵族,后来因为家族成员受到圣殿骑士的杀害,决心成为一名刺客.最终,凭借着他的努力和出众的天赋,成为了杰出的刺 ...
- JZOJ 3296 Luogu P3296 [SDOI2013]刺客信条
前言 做法来自:@pzrpzr ,写一下!Orz pzr! 题目大意 \(n\) 个点的无根树,每个点有两个 \(0/1\) 权值,合适地安排节点在同构树中的顺序,使得前后对应的权值不同节点个数最小, ...
- 【JZOJ3296】【SDOI2013】刺客信条(assassin)
╰( ̄▽ ̄)╭ Description 故事发生在1486 年的意大利,Ezio 原本只是一个文艺复兴时期的贵族,后来因为家族成员受到圣殿骑士的杀害,决心成为一名刺客.最终,凭借着他的努力和出众的天赋 ...
- BZOJ3130: [Sdoi2013]费用流[最大流 实数二分]
3130: [Sdoi2013]费用流 Time Limit: 10 Sec Memory Limit: 128 MBSec Special JudgeSubmit: 960 Solved: 5 ...
- Unity实现刺客信条灯光的思路探究
灯光需求 类似刺客信条的开场CG动画,场景中打着酷炫的灯光,玩家在场景中行走可以感受到灯光很真实. 参考视频:http://www.iqiyi.com/w_19rqytbmvt.html 运行环境 安 ...
随机推荐
- 如何理解CUDA中的cudaMalloc()的参数
首先看下此运行时函数的原型: cudaError_t cudaMalloc (void **devPtr, size_t size ); 主要的第一个参数.为什么是两个星星呢?用个例子来说明下. fl ...
- 3-MySQL高级-事务-命令(2)
事务命令 表的引擎类型必须是innodb类型才可以使用事务,这是mysql表的默认引擎 查看表的创建语句,可以看到engine=innodb -- 选择数据库 use jing_dong; -- 查看 ...
- Spring 基于xml配置方式的事务(14)
参考前面的声明式事务的例子:http://www.cnblogs.com/caoyc/p/5632198.html 我们做了相应的修改.在dao中和service中的各个类中,去掉所有注解标签.然后为 ...
- NEO4J亿级数据全文索引构建优化
NEO4J亿级数据全文索引构建优化 一.数据量规模(亿级) 二.构建索引的方式 三.构建索引发生的异常 四.全文索引代码优化 1.Java.lang.OutOfMemoryError 2.访问数据库时 ...
- vue axios简单配置
参考:https://www.cnblogs.com/sophie_wang/p/7844119.html 1. 安装 npm install axios 2. main.js import axio ...
- spring在普通类中获取session和request
在使用spring时,经常需要在普通类中获取session,request等对像.比如一些AOP拦截器类,在有使用struts2时,因为struts2有一个接口使用org.apache.struts2 ...
- list集合排序3
java list按照元素对象的指定多个字段属性进行排序 转载 2016年12月27日 11:39:02 见: http://blog.csdn.net/enable1234___/article/d ...
- 神经网络 (1)- Alexnet
文章目录 模型结构 conv1层 conv2层 conv3层 conv4层 conv5层 FC6全链接图: fc7全连接层:和fc6类似. fc8链接层: 模型优化 选择ReLU作为激活函数 多GPU ...
- MySQL数据库之DQL(数据查询语言)
1.MySQL之DQL查询AS CONCAT LIKE的使用 (1)select 列名1,列名2,...... from 表名 [where 条件] 查询所有字段用*,不带where条件的话,就会把表 ...
- LeetCode第二题—— Add Two Numbers(模拟两数相加)
Description: You are given two non-empty linked lists representing two non-negative integers. The di ...