【JZOJ3296】【BZOJ3197】【luoguP3296】刺客信条

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;
}
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页