description
故事发生在1486 年的意大利,Ezio 原本只是一个文艺复兴时期的贵族,后来因为家族成员受到圣殿骑士的杀害,决心成为一名刺客。最终,凭借着他的努力和出众的天赋,成为了杰出的刺客大师,他不仅是个身手敏捷的武林高手,飞檐走壁擅长各种暗杀术。刺客组织在他的带领下,为被剥削的平民声张正义,赶跑了原本统治意大利的圣殿骑士首领-教皇亚历山大六世。在他的一生中,经历了无数次惊心动魄、扣人心弦的探险和刺杀。
曾经有一次,为了寻找Altair 留下的线索和装备,Ezio 在佛罗伦萨中的刺客墓穴进行探索。这个刺客墓穴中有许多密室,且任何两个密室之间只存在一条唯一的路径。这些密室里都有一个刺客标记,他可以启动或者关闭该刺客标记。为了打开储存着线索和装备的储藏室,Ezio 必须操作刺客标记来揭开古老的封印。要想解开这个封印,他需要通过改变某些刺客标记的启动情况,使得所有刺客标记与封印密码“看起来一样”。在这里,“看起来一样”
的定义是:存在一种“标记”密室与“密码”密室之间一一对应的关系,使得密室间的连接情况和启动情况相同(提示中有更详细解释)。幸运的是,在Ezio 来到刺客墓穴之前,在Da Vinci 的帮助下,Ezio 已经得知了打开储藏室所需要的密码。
而你的任务则是帮助Ezio 找出达成目标所需要最少的改动标记次数。
analysis
-
就冲这题面肛整天都值得
-
考虑先找出树的重心,这个 D P DP DP应该没人不会
-
用哈希把树给搞一下,然后可以直接通过查询哈希值是否相等判断树是否同构
-
预处理一下重心为根的树,然后从树上任意一点为根开始遍历整棵树,从下往上一层层 D P DP DP
-
设 f [ i ] [ j ] f[i][j] f[i][j]表示任意点为根的树中 i i i号点、重心为根的树中 j j j号点的最小代价值
-
叶子结点的 f f f当然是它和目标状态是否相同,从叶子结点向上回溯
-
类似树形 D P DP DP,对于一个节点的儿子中同构的儿子,让它们两两配对,使代价越小越好
-
不就是二分图权值最小匹配,直接上 z k w zkw zkw费用流跑就好了
-
S , T S,T S,T连出流量 1 1 1费用 0 0 0的边,两棵树两两儿子节点间连流量 1 1 1费用 f [ 儿 子 1 ] [ 儿 子 2 ] f[儿子1][儿子2] f[儿子1][儿子2]的边
-
然后就没了
-
该代码成功垫底本子 O J OJ OJ
-
其实我打的哈希有点假,取错模数会 G G GG GG,应该打双哈希,只不过我很懒……
code
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#define MAXN 1405
#define mod 19260817
#define INF 1000000007
#define ll long long
#define fo(i,a,b) for (ll i=a;i<=b;++i)
#define fd(i,a,b) for (ll i=a;i>=b;--i)
#define rep(i,a) for (ll i=last[a];i;i=next[i])
#define rep1(i,a) for (ll i=las[a];i;i=nxt[i])
using namespace std;
ll f[705][705],map1[705][705],map2[705][705];
ll last[MAXN],next[MAXN],tov[MAXN],cur[MAXN];
ll las[MAXN],nxt[MAXN],to[MAXN],len1[MAXN],len2[MAXN];
ll g1[MAXN],g2[MAXN],depth[MAXN];
ll hash[MAXN],size[MAXN];
bool bz[MAXN];
ll n,tot,total,tmp,root,ans=INF,S,T,answer;
inline ll read()
{
ll x=0,f=1;char ch=getchar();
while (ch<'0' || '9'<ch){if (ch=='-')f=-1;ch=getchar();}
while ('0'<=ch && ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
inline bool cmp(ll x,ll y)
{
return hash[x]<hash[y];
}
inline void link(ll x,ll y)
{
next[++tot]=last[x],last[x]=tot,tov[tot]=y;
}
inline void find_root(ll x)
{
ll mx=0;size[x]=1,bz[x]=0;
rep(i,x)if (bz[tov[i]])
{
find_root(tov[i]);
size[x]+=size[tov[i]];
mx=max(mx,size[tov[i]]);
}
mx=max(mx,n-size[x]);
if (mx<tmp)tmp=mx,root=x;
}
inline void dfs1(ll x,ll y)
{
hash[x]=100007;
rep(i,x)if (tov[i]!=y)
{
dfs1(tov[i],x);
map1[x][++map1[x][0]]=tov[i];
}
sort(map1[x]+1,map1[x]+map1[x][0]+1,cmp);
fo(i,1,map1[x][0])hash[x]=((hash[x]*mod)*hash[map1[x][i]])%INF;
}
inline void dfs2(ll x,ll y)
{
hash[x]=100007,size[x]=1;
rep(i,x)if (tov[i]!=y)
{
dfs2(tov[i],x),size[x]+=size[tov[i]];
map2[x][++map2[x][0]]=tov[i];
}
sort(map2[x]+1,map2[x]+map2[x][0]+1,cmp);
fo(i,1,map2[x][0])hash[x]=((hash[x]*mod)*hash[map2[x][i]])%INF;
}
inline void link_edge(ll x,ll y,ll z1,ll z2)
{
nxt[++total]=las[x],las[x]=total,to[total]=y,len1[total]=z1,len2[total]=z2;
}
inline bool spfa()
{
memset(bz,1,sizeof(bz));
memset(depth,127,sizeof(depth));
deque<ll>q;q.push_front(T);
depth[T]=bz[T]=0;
while (!q.empty())
{
ll now=q.front();q.pop_front();
rep1(i,now)if (len1[i^1] && depth[now]+len2[i^1]<depth[to[i]])
{
depth[to[i]]=depth[now]+len2[i^1];
if (bz[to[i]])
{
bz[to[i]]=0;
if (!q.empty() && depth[to[i]]<depth[q.front()])q.push_front(to[i]);
else q.push_back(to[i]);
}
}
bz[now]=1;
}
return depth[S]<INF;
}
inline ll dfs(ll x,ll flow)
{
if (x==T){bz[T]=0;return flow;}
ll used=0;bz[x]=0;
for (ll i=cur[x];i;i=nxt[i])
{
cur[x]=i;
if (len1[i] && bz[to[i]] && depth[to[i]]+len2[i]==depth[x])
{
ll tmp=dfs(to[i],min(len1[i],flow-used));
if (tmp)answer+=len2[i]*tmp,len1[i]-=tmp,len1[i^1]+=tmp,used+=tmp;
if (used==flow)break;
}
}
return used;
}
inline ll zkwflow()
{
ll flow=0;answer=0;
while (spfa())
{
bz[T]=0;
while (!bz[T])
{
memset(bz,1,sizeof(bz));
memcpy(cur,las,sizeof(cur));
flow+=dfs(S,INF);
}
}
return flow;
}
inline ll dp(ll x,ll y)
{
if (f[x][y]!=-1)return f[x][y];
f[x][y]=g1[x]^g2[y];ll i=1;
if (size[x]==1)return f[x][y];
while (i<=map2[x][0])
{
ll j=i;
while (j<map2[x][0] && hash[map2[x][i]]==hash[map2[x][j+1]])++j;
fo(k,i,j)fo(l,i,j)dp(map2[x][k],map1[y][l]);
S=2*n+1,T=2*n+2,total=1;
memset(las,0,sizeof(las));
memset(nxt,0,sizeof(nxt));
memset(to,0,sizeof(to)),total=1;
memset(len1,0,sizeof(len1));
memset(len2,0,sizeof(len2));
fo(k,i,j)
{
link_edge(S,map2[x][k],1,0),link_edge(map2[x][k],S,0,0);
link_edge(map1[y][k]+n,T,1,0),link_edge(T,map1[y][k]+n,0,0);
fo(l,i,j)
{
ll tmp=dp(map2[x][k],map1[y][l]);
link_edge(map2[x][k],map1[y][l]+n,1,tmp);
link_edge(map1[y][l]+n,map2[x][k],0,-tmp);
}
}
zkwflow(),f[x][y]+=answer;i=j+1;
}
return f[x][y];
}
int main()
{
n=read();
fo(i,1,n-1)
{
ll x=read(),y=read();
link(x,y),link(y,x);
}
fo(i,1,n)g1[i]=read();fo(i,1,n)g2[i]=read();
memset(bz,1,sizeof(bz)),tmp=INF,find_root(1);
dfs1(root,0),tmp=hash[root];
fo(i,1,n)
{
memset(hash,0,sizeof(hash));
memset(map2,0,sizeof(map2));
dfs2(i,0);
if (hash[i]==tmp)
{
memset(f,-1,sizeof(f));
ans=min(ans,dp(i,root));
}
}
printf("%lld\n",ans);
return 0;
}