UOJ#210. 【UER #6】寻找罪犯 2-sat

#210. 【UER #6】寻找罪犯

链接:http://uoj.ac/problem/210

想法:2-sat模型。每个人拆点,分别表示为犯人、非犯人。每个句供词拆点,分别表示真话、假话。供词与对应人的点连双向边。假如$x_i$非犯人,那么连向他的所有真供词。一个假供词意味着至少一个犯人以及这个人的其他供词为真。于是连边。优化连边就可以用前缀,后缀的方式优化。

方案的输出参见:论文 博客

复杂度$O(n+m)$,常数有点大...

 

#include< algorithm >
#include< cstdio >
#include< vector >

#define gec getchar
#define FILE(F) freopen(F".in","r",stdin),freopen(F".out","w",stdout)
#define DEBUG fprintf(stderr,"Passing [%s] in Line (%d)\n",__FUNCTION__,__LINE__);

typedef long long ll;
template
inline void read(T&x)
{
	x=0;bool f=0;char c=gec();
	for(;c<'0'||c>'9';c=gec())f=(c=='-');
	for(;c>='0'&&c<='9';c=gec())x=x*10+c-'0';
	x=f?-x:x;
}
const int N(100010),M(100010),MAXN((N+M+M)<<1),MAXM(M*20);
int n,m,x,y,t;
std::vectorconf[N];
//(i<<1)为正点,(i<<1)|1为负点 [2,n<<1|1]为犯人,[n<<1+2,n<<1|1+(m<<1|1)]为供词
struct Node{int nd,nx;}bot[MAXM];
int tot,first[MAXN],total,col[MAXN],ant,limt;
void add(int a,int b)
{bot[++tot]=(Node){b,first[a]};first[a]=tot;}
void addedge(int a,int b){add(a,b);add(b,a);}
int min(int a,int b){return a>b?b:a;}
namespace Tarjan
{
	int low[MAXN],dfn[MAXN],st[MAXN],tp,cnt;bool vis[MAXN],flag[MAXN];
	void Dfs(int x)
	{
		low[x]=dfn[x]=++cnt; vis[x]=flag[x]=1; st[++tp]=x;
		for(int v=first[x];v;v=bot[v].nx)
		{
			if(!vis[bot[v].nd])
			{
				Dfs(bot[v].nd);
				low[x]=min(low[x],low[bot[v].nd]);
			}else if(flag[bot[v].nd])low[x]=min(low[x],dfn[bot[v].nd]);
		}
		if(low[x]==dfn[x])
	 		for(ant++;flag[x];tp--)flag[st[tp]]=0,col[st[tp]]=ant;
	}
	void run()
	{
		for(int i=2;i<=total;i++)
		if(!vis[i])Dfs(i);
	}
}
namespace Coloring
{
	std::vectorbelong[MAXN];
	int nx[MAXM],nd[MAXM],head[MAXN],color[MAXN],in[MAXN],tot,st[MAXN],tp,now,no;
	//color 1:不选,2选,0未知
	void add(int a,int b)
	{nd[++tot]=b; nx[tot]=head[a]; head[a]=tot; in[b]++;}
	bool Get_plan()
	{
		for(int i=1;i<=n+m;i++)
		if(col[i<<1]==col[i<<1|1])return false;
		for(int i=2;i<=total;i++)
		{
			if(i<=limt)belong[col[i]].push_back(i);//不用管虚点
			for(int v=first[i];v;v=bot[v].nx)
			if(col[i]!=col[bot[v].nd])add(col[bot[v].nd],col[i]);
		}
		for(int i=1;i<=ant;i++)
		if(!in[i])st[++tp]=i;
		while(tp)
		{
			now=st[tp--];
			no=color[now];
			for(int v=0,sz=belong[now].size();v<sz;v++)
			if(color[col[belong[now][v]^1]]==2)no=1;
			color[now]=no?1:2;
			for(int v=head[now];v;v=nx[v])
			{
				in[nd[v]]--; color[nd[v]]|=no;
				if(!in[nd[v]])st[++tp]=nd[v];
			}
		}
		for(int i=1;i<=n;i++)
		if(color[col[i<<1|1]]==2)st[++tp]=i;
		std::sort(st+1,st+1+tp);
		printf("%d\n",tp);
		for(int i=1;i<=tp;i++)
		printf("%d ",st[i]);
		return true;
	}
	
}
int main()
{
#ifndef ONLINE_JUDGE
	FILE("C");
#endif		
	read(n);read(m);
	for(int i=1,now;i<=m;i++)
	{
		read(x),read(y),read(t);
		now=(i+n)<<1;
		conf[x].push_back(now);
		addedge(now,(y<<1)|(t^1));
		addedge(now|1,(y<<1)|t);
	}
	limt=total=(n+m)<<1|1;
	for(int i=1,last;i<=n;i++)
	{
		/*for(int v=0,sz=conf[i].size();v<sz;v++)
		{
			for(int u=0;u<sz;u++)
			if(u!=v)add(conf[i][v]^1,conf[i][u]);
			add(conf[i][v]^1,i<<1|1);
			add(i<<1,conf[i][v]);
		}*/
		last=0;
		for(int v=0,sz=conf[i].size();v<sz;v++)
		{
			if(last)add(conf[i][v]^1,last);
			if(last)add(last+1,last);
			add(last=++total,conf[i][v]);
			add(conf[i][v]^1,i<<1|1);
		}
		last=0;
		for(int sz=conf[i].size(),v=sz-1;v>=0;v--)
		{
			if(last)add(conf[i][v]^1,last);
			if(last)add(last+1,last);
			add(last=++total,conf[i][v]);
			add(i<<1,conf[i][v]);
		}
	}
	Tarjan::run();
	if(!Coloring::Get_plan())printf("Impossible");
	return 0;
}
posted @ 2017-06-11 21:17  Oncle_Ha  阅读(193)  评论(0编辑  收藏  举报