POJ.3207.Ikki's Story IV-Panda's Trick(2-SAT)

题目链接

(Description)

一个圆上顺序排列0,1,...,n-1共n个点,给出m条线段,线段可以从里面连也可以从外面连,问是否能满足所有线段不相交

(Solution)

把每条线段看做一个点,只有在圆外和在圆内两种情况,于是可以把线段拆成i,i',分别表示两种情况
对于端点相交的线段会产生矛盾
若i不能与j相交,则:
i在圆内 j就在圆外;i在圆外 j就在圆内
j在圆内 i就在圆外;j在圆外 i就在圆内
注意这个要双向判断
边数一定要够
为什么不直接用x+m表示另一个点。。zz了

//1816K	16MS
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
const int N=2005,M=100005;

int n,m,st[505],ed[505],Enum,H[N],nxt[M<<1],to[M<<1],dfn[N],low[N],id,top,sk[N],cnt,bel[N];
bool ins[N];

inline int read()
{
	int now=0;register char c=gc();
	for(;!isdigit(c);c=gc());
	for(;isdigit(c);now=now*10+c-'0',c=gc());
	return now;
}
inline void AddEdge(int u,int v){
	to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
}
void Tarjan(int x)
{
	dfn[x]=low[x]=++id, sk[++top]=x, ins[x]=1;
	for(int v,i=H[x]; i; i=nxt[i])
		if(!dfn[v=to[i]]) Tarjan(v), low[x]=std::min(low[x],low[v]);
		else if(ins[v]) low[x]=std::min(low[x],dfn[v]);
	if(dfn[x]==low[x])
	{
		++cnt;
		do{
			ins[sk[top]]=0, bel[sk[top--]]=cnt;
		}while(x!=sk[top+1]);
	}
}

int main()
{
	n=read(),m=read();
	for(int i=1; i<=m; ++i)
	{
		st[i]=read(),ed[i]=read();
		if(st[i]>ed[i]) std::swap(st[i],ed[i]);
	}
	for(int i=1; i<=m; ++i)
		for(int j=1; j<=m; ++j)
			if(i!=j && st[j]<st[i]&&st[i]<ed[j]&&ed[j]<ed[i])//不同的相交顺序就由都枚举一遍解决了 
				AddEdge(i*2-1,j*2), AddEdge(i*2,j*2-1), AddEdge(j*2-1,i*2), AddEdge(j*2,i*2-1);
	for(int i=1; i<=m<<1; ++i)
		if(!dfn[i]) Tarjan(i);
	bool f=1;
	for(int i=1; i<=m; ++i)
		if(bel[i*2-1]==bel[i<<1]) {f=0; break;}
	puts(f?"panda is telling the truth...":"the evil panda is lying again");

	return 0;
}
原文地址:https://www.cnblogs.com/SovietPower/p/8485995.html