首页 > 代码库 > BZOJ 3197 Sdoi2013 assassin 动态规划+树同构+费用流

BZOJ 3197 Sdoi2013 assassin 动态规划+树同构+费用流

题目大意:给定一棵树和两组权值,求第一组权值最少改变多少个之后这棵树经过重标号之后与第二组权值相同

这个题做法很神- -

首先和3162一样的处理方式 我们先找到这棵树的重心作为根 如果重心有两个就新建一个根连向这两个重心

令f[x][y]表示x所在子树的第一组权值和y所在子树的第二组权值匹配的最小花销

转移的必要条件是x所在的子树与y所在的子树同构且x与y深度相同

为了保证无后效性,x的所有子节点与y的所有子节点之间的最小花销必须都已求出

那么我们将节点以深度的倒数为第一键值,Hash值为第二键值排序,从左到右枚举深度与Hash相同的点转移

转移利用的是二分图最大权完备匹配- - 这个简直- -

每次转移x到y 我们需要将x与y同构的子树之间一一匹配 于是我们可以利用二分图最大权完备匹配来搞这东西- -

不会KM啊QAQ 于是写了费用流- -

代码写了4.6KB居然没怎么调就1A了- - 真是感人- -

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 710
#define ORIGIN 233
#define BASE 233333333
#define S 0
#define T 29
#define INF 0x3f3f3f3f
using namespace std;
int n,f1[M],f2[M],f[M][M];
pair<pair<int,unsigned long long>,int> sorter[M];
//第一键值深度,第二键值哈希值
namespace Trees_Isomorphism{
	struct abcd{
		int to,next;
	}table[M<<1];
	int head[M],tot=1;
	int root,cg[2];
	int fa[M],size[M],dpt[M];
	unsigned long long hash[M];
	void Add(int x,int y)
	{
		table[++tot].to=y;
		table[tot].next=head[x];
		head[x]=tot;
	}
	void Get_Centre_Of_Gravity(int x,int from)
	{
		int i,flag=1;
		size[x]=1;
		for(i=head[x];i;i=table[i].next)
			if(table[i].to!=from)
			{
				Get_Centre_Of_Gravity(table[i].to,x);
				size[x]+=size[table[i].to];
				if(size[table[i].to]<<1>n)
					flag=0;
			}
		if(n-size[x]<<1>n)
			flag=0;
		if(flag)
			(cg[0]?cg[1]:cg[0])=x;
	}
	void Get_Hash(int x)
	{
		static int stack[M];
		int i,top=0;
		dpt[x]=dpt[fa[x]]+1;
		for(i=head[x];i;i=table[i].next)
			if(table[i].to!=fa[x])
			{
				fa[table[i].to]=x;
				Get_Hash(table[i].to);
			}
		for(i=head[x];i;i=table[i].next)
			if(table[i].to!=fa[x])
				stack[++top]=hash[table[i].to];
		sort(stack+1,stack+top+1);
		hash[x]=ORIGIN;
		for(i=1;i<=top;i++)
			(((hash[x]*=BASE)^=stack[i])+=stack[i])^=stack[i];
	}
}
namespace Min_Cost_Max_Flow{
	struct abcd{
		int to,flow,cost,next;
	}table[10100];
	int head[30],tot=1;
	int ans;
	void Initialize()
	{
		memset(head,0,sizeof head);
		tot=1;ans=0;
	}
	void Add(int x,int y,int f,int c)
	{
		table[++tot].to=y;
		table[tot].flow=f;
		table[tot].cost=c;
		table[tot].next=head[x];
		head[x]=tot;
	}
	void Link(int x,int y,int f,int c)
	{
		Add(x,y,f,c);
		Add(y,x,0,-c);
	}
	bool Edmonds_Karp()
	{
		static int q[65540],flow[30],cost[30],from[30];
		static unsigned short r,h;
		static bool v[M];
		int i;
		memset(cost,0x3f,sizeof cost);
		flow[S]=INF;cost[S]=0;q[++r]=S;
		while(r!=h)
		{
			int x=q[++h];v[x]=0;
			for(i=head[x];i;i=table[i].next)
				if(table[i].flow&&cost[table[i].to]>cost[x]+table[i].cost)
				{
					cost[table[i].to]=cost[x]+table[i].cost;
					flow[table[i].to]=min(flow[x],table[i].flow);
					from[table[i].to]=i;
					if(!v[table[i].to])
						v[table[i].to]=1,q[++r]=table[i].to;
				}
		}
		if(cost[T]==INF) return false;
		ans+=cost[T]*flow[T];
		for(i=from[T];i;i=from[table[i^1].to])
			table[i].flow-=flow[T],table[i^1].flow+=flow[T];
		return true;
	}
}
using namespace Trees_Isomorphism;
bool Compare(int x,int y)
{
	return hash[x] < hash[y];
}
int DP(int x,int y)
{
	static int stack1[M],stack2[M];
	int i,j,k,l,top1=0,top2=0;
	for(i=head[x];i;i=table[i].next)
		if(table[i].to!=fa[x])
			stack1[++top1]=table[i].to;
	for(i=head[y];i;i=table[i].next)
		if(table[i].to!=fa[y])
			stack2[++top2]=table[i].to;
	sort(stack1+1,stack1+top1+1,Compare);
	sort(stack2+1,stack2+top2+1,Compare);
	Min_Cost_Max_Flow::Initialize();
	for(i=1;i<=top1;i=j)
	{
		for(j=i+1;j<=top1&&hash[stack1[i]]==hash[stack1[j]];j++);
		for(k=i;k<j;k++)
			for(l=i;l<j;l++)
				Min_Cost_Max_Flow::Link(k,top1+l,1,f[stack1[k]][stack2[l]]);
	}
	for(i=1;i<=top1;i++)
	{
		Min_Cost_Max_Flow::Link(S,i,1,0);
		Min_Cost_Max_Flow::Link(i+top1,T,1,0);
	}
	while( Min_Cost_Max_Flow::Edmonds_Karp() );
	return Min_Cost_Max_Flow::ans+(f1[x]^f2[y]);
}
int main()
{
	int i,j,x,y;
	cin>>n;
	for(i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		Add(x,y);Add(y,x);
	}
	for(i=1;i<=n;i++)
		scanf("%d",&f1[i]);
	for(i=1;i<=n;i++)
		scanf("%d",&f2[i]);
	Get_Centre_Of_Gravity(1,0);
	if(cg[1])
	{
		root=++n;
		for(i=head[cg[0]];i;i=table[i].next)
			if(table[i].to==cg[1])
			{
				table[i].to=table[i^1].to=n;
				break;
			}
		Add(n,cg[0]);
		Add(n,cg[1]);
	}
	else root=cg[0];
	Get_Hash(root);
	for(i=1;i<=n;i++)
		sorter[i]=make_pair(make_pair(-dpt[i],hash[i]),i);
	sort(sorter+1,sorter+n+1);
	for(i=1;i<=n;i=j)
	{
		for(j=i+1;j<=n&&sorter[i].first==sorter[j].first;j++);
		for(x=i;x<j;x++)
			for(y=i;y<j;y++)
				f[sorter[x].second][sorter[y].second]=DP(sorter[x].second,sorter[y].second);
	}
	cout<<f[root][root]<<endl;
	return 0;
}


BZOJ 3197 Sdoi2013 assassin 动态规划+树同构+费用流