链接:http://poj.org/problem?id=3207
题意:n个点,围成一个圆编号从0到n-1。现在要给m对点连线,可以在圆内部连也可以在圆外部连。问有无可能使这m条线不交叉(有一个共同端点不算交叉。)
思路:很明显的2-SAT题,两个点要么在内部连,要么在外部连,等同于集合中的两个元素,两条线交叉代表有互斥关系。直接跑2-SAT,判断一个集合的两个元素是否在同一强连通分量即可。
PS:注意建图,建图,建图。。。。。。。。。。。。。。。。。。。。。
#include <cstdio>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
const int N = 1e3+10;
const int M = 1e7+10;
int n,m;
struct Pair
{
int s,e;
}ps[N];
struct Edge
{
int to,nxt;
};
struct Two_Sat
{
int head[N],cnt,dfn[N],id,low[N],color[N],cl,sta[N],top;
bool vis[N];
struct Edge G[M];
inline void init()
{
cnt=id=top=cl=0;
for(int i=0;i<2*m;i++) head[i]=-1,dfn[i]=vis[i]=0;
return ;
}
inline void add(int u,int v)
{
G[cnt].to=v; G[cnt].nxt=head[u]; head[u]=cnt++;
return ;
}
inline void tarjan(int u)
{
int v;
dfn[u]=low[u]=++id;
sta[++top]=u; vis[u]=1;
for(int i=head[u];i!=-1;i=G[i].nxt)
{
v=G[i].to;
if(!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(vis[v])
low[u]=min(low[u],low[v]);
}
if(low[u]==dfn[u])
{
color[u]=++cl;
while(sta[top]!=u)
{
color[sta[top]]=cl; vis[sta[top--]]=0;
}
vis[sta[top--]]=0;
}
return ;
}
inline void Tarjan()
{
for(int i=0;i<2*m;i++)
if(!dfn[i]) tarjan(i);
return ;
}
inline void solve()
{
for(int i=0;i<2*m;i+=2)
{
if(color[i]==color[i^1])
{
printf("the evil panda is lying again\n");
return ;
}
}
printf("panda is telling the truth...\n");
}
}two;
inline bool ok(int s1,int e1,int s2,int e2)
{
//交叉的两条线,假设固定一条线,那么另一条线的两个端点一定在这条线的
//两侧,并且不包括这条线的两个端点。
if((s2<s1&&s1<e2)&&((s2>e1&&e1<e2)||(s2<e1&&e1>e2))) return 1;
if((s2<e1&&e1<e2)&&((s2>s1&&s1<e2)||(s2<s1&&s1>e2))) return 1;
return 0;
}
int main(void)
{
scanf("%d%d",&n,&m);
two.init();
for(int i=0;i<2*m;i+=2)
{
scanf("%d%d",&ps[i].s,&ps[i].e);
if(ps[i].e<ps[i].s) swap(ps[i].s,ps[i].e);
}
for(int i=0;i<2*m;i+=2)
for(int j=i+2;j<2*m;j+=2)
{
if(ok(ps[i].s,ps[i].e,ps[j].s,ps[j].e))
{
two.add(i,j^1);two.add(j,i^1);
two.add(j^1,i);two.add(i^1,j);
}
}
two.Tarjan();
two.solve();
return 0;
}