【XSY2750】Mythological V 2-sat

题目描述

  有一棵\(n\)个点的树,还有\(m\)个物品。

  你要把每个物品放在树上的一个点上(两个物品可以放在同一个点)。

  有\(q\)个限制:\(a,b\)两个物品在路上的最短路经过\(c\)

  要你构造一组合法的方案。

  \(n,m\leq 250\)

题解

  很容易想到2-sat。

  但是把点看成"物品\(x\)放在\(y\)上"会找不到合法解。

  所以要把点看成“物品\(x\)在以\(y\)为根的子树内”,这样根就是必须选的。

  连边的话(下面只列出一半的边):\(x\rightarrow f_x,x\rightarrow !y\)(\(y\)\(x\)的兄弟)(这些边可以弄一些前缀后缀的点优化)

  和询问有关的:

  1.\(a\neq b\)\((a,x)\rightarrow !(b,x)\)(\(x\)\(c\)的儿子),\(!(a,c)\rightarrow (b,c)\)

  2.\(a=b\)\(!(a,c)\rightarrow(a,c)\),\((a,x)\rightarrow !(a,x)\)(\(x\)\(c\)的儿子)。

  点数为\(O(nm)\)

  边数为\(O(nm^2)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
struct graph
{
	int h[400010];
	int v[40000010];
	int t[40000010];
	int n;
	graph()
	{
		n=0;
		memset(h,0,sizeof h);
	}
	void add(int x,int y)
	{
		n++;
		v[n]=y;
		t[n]=h[x];
		h[x]=n;
	}
};
graph g;
vector<int> t[260],t2;
int n,m,q;
int id(int x,int y)//x=物品,y=点
{
	return (y-1)*m+x;
}
int cnt;
int pre[260],suf[260];
void add0(int k1,int x,int k2,int y)
{
	g.add(id(k1,x)*2-1,id(k2,y)*2);
	g.add(id(k2,y)*2-1,id(k1,x)*2);
}
void add4(int k1,int x,int k2,int y)
{
	g.add(id(k1,x)*2,id(k2,y)*2-1);
	g.add(id(k2,y)*2,id(k1,x)*2-1);
}
void add1(int k1,int x,int k2,int y)
{
	g.add(id(k1,x)*2-1,id(k2,y)*2-1);
	g.add(id(k2,y)*2,id(k1,x)*2);
}
void add2(int k,int x)
{
	g.add(id(k,x)*2,id(k,x)*2-1);
}
void add3(int k,int x)
{
	g.add(id(k,x)*2-1,id(k,x)*2);
}
int f[300];
int d[300];
void dfs(int x,int fa,int dep)
{
	t2.clear();
	f[x]=fa;
	d[x]=dep;
	for(auto v:t[x])
		if(v!=fa)
			t2.push_back(v);
	t[x]=t2;
	for(auto v:t[x])
		dfs(v,x,dep+1);
}
int b[400010];
int e[400010];
int ti,tot,top;
int st[400010];
int dfn[400010];
int low[400010];
int c[300][300][300];
void dfs(int x)
{
	low[x]=dfn[x]=++ti;
	st[++top]=x;
	b[x]=1;
	for(int i=g.h[x];i;i=g.t[i])
	{
		if(b[g.v[i]]!=2)
		{
			if(!b[g.v[i]])
				dfs(g.v[i]);
			low[x]=min(low[x],low[g.v[i]]);
		}
	}
	if(low[x]>=dfn[x])
	{
		int v;
		tot++;
		do
		{
			v=st[top--];
			e[v]=tot;
			b[v]=2;
		}
		while(v!=x);
	}
}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
#endif
	scanf("%d%d%d",&n,&m,&q);
	int x,y,z;
	for(int i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		t[x].push_back(y);
		t[y].push_back(x);
	}
	dfs(1,0,1);
	cnt=n;
	for(int i=1;i<=n;i++)
	{
		if(i!=1)
			for(int k=1;k<=m;k++)
				add1(k,i,k,f[i]);
		int sz=t[i].size();
		for(int j=0;j<sz;j++)
		{
			pre[j]=++cnt;
			for(int k=1;k<=m;k++)
				add1(k,t[i][j],k,pre[j]);
			if(j)
				for(int k=1;k<=m;k++)
					add1(k,pre[j-1],k,pre[j]);
		}
		for(int j=sz-1;j>=0;j--)
		{
			suf[j]=++cnt;
			for(int k=1;k<=m;k++)
				add1(k,t[i][j],k,suf[j]);
			if(j!=sz-1)
				for(int k=1;k<=m;k++)
					add1(k,suf[j+1],k,suf[j]);
		}
		for(int j=0;j<sz;j++)
		{
			if(j)
				for(int k=1;k<=m;k++)
					add0(k,t[i][j],k,pre[j-1]);
			if(j!=sz-1)
				for(int k=1;k<=m;k++)
					add0(k,t[i][j],k,suf[j+1]);
		}
	}
	for(int i=1;i<=m;i++)
		add2(i,1);
	memset(b,0,sizeof b);
	for(int i=1;i<=q;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		if(x>y)
			swap(x,y);
		if(c[x][y][z])
			continue;
		c[x][y][z]=1;
		if(x==y)
		{
			add2(x,z);
			for(auto v:t[z])
				add3(x,v);
		}
		else
		{
			for(auto v:t[z])
				add0(x,v,y,v);
			add4(x,z,y,z);
		}
	}
	ti=0;
	tot=0;
	top=0;
	for(int i=1;i<=2*m*cnt;i++)
		if(!b[i])
			dfs(i);
	for(int i=1;i<=m;i++)
	{
		int ans=1;
		for(int j=1;j<=n;j++)
			if(e[2*id(i,j)-1]<e[2*id(i,j)])
				if(d[j]>d[ans])
					ans=j;
		printf("%d ",ans);
	}
	return 0;
}
posted @ 2018-03-19 08:45  ywwyww  阅读(255)  评论(0编辑  收藏  举报