题目大意:
一个圆盘上有n个点,编号顺次为0……n-1,现在给出m条线段,这些线段可以在盘的正面,也可以在反面,要求判断最终是否有线段不相交的安排,使满足要求.
思路分析:
令第i条线(i从0开始)在正面为第2i个顶点,在反面为第2i+1个顶点.若第i条线和第j条线构成一个四边形的两条对角线,则在2i和2j+1之间连一条双向边,2i+1和2j之间连一条双向边.这样建图后求强连通分量,看点2i和点2i+1是否在同一个强连通分量中,如果是在同一强连通分量中,就说明不满足要求,如果没有则满足.
首先,我们知道若第i条线和第j条线构成一个四边形的两条对角线,则2i和2j不相容,又2i和2i+1中必须出现一个,且2j和2j+1中也必须出现一个,所以可以说2i+1和2j+1中至少出现一个,换句话说, 从2i到2j+1连一条有向边表示如果选择了2i就必须选择2j+1,从2j到2i+1连一条有向边表示如果选择了2j就必须选择2i+1,同理对于2i+1和2j+1的不相容也可以连两条有向边,所以最后跟上面的组成了两条双向边.
CODE:
/*POJ第一道2-SAT*/
/*每一条线看成图上一个点*/
/*AC代码:0ms*/
#include <iostream>
#define MAXN 1005
#define MAXM 200000
struct edge
{
int v,next;
edge(){}
edge(int v1,int next1):v(v1),next(next1){}
}E[MAXM];
int head[MAXN],ecnt;
int Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN];
bool Instack[MAXN];
int Index,cnt,top,N,M;
int a[MAXN],b[MAXN];
void Insert(int u,int v)
{
E[ecnt]=edge(v,head[u]);
head[u]=ecnt++;
}
void swap(int &a,int &b)
{
if(a>b)
{int t=a;a=b;b=t;}
}
void Build_Map()//构图
{
int i,j;
memset(head,-1,sizeof(head));
ecnt=0;
for(i=0;i<M;i++)
{
scanf("%d%d",&a[i],&b[i]);
swap(a[i],b[i]);//节点是顺时针排列的
}
for(i=0;i<M;i++)
{
for(j=i+1;j<M;j++)
{
//判断编号为i,j的两条线段是否相交
if((a[i]<a[j]&&b[i]<b[j]&&a[j]<b[i])||(a[i]>a[j]&&b[i]>b[j]&&a[i]<b[j]))
{
Insert(2*i,2*j+1);
Insert(2*j+1,2*i);//不能去掉
Insert(2*j,2*i+1);
Insert(2*i+1,2*j);
}
}
}
}
void Tarjan(int u)
{
int v,i;
Low[u]=DFN[u]=++Index;
Stack[++top]=u;
Instack[u]=true;
for(i=head[u];i!=-1;i=E[i].next)
{
v=E[i].v;
if(!DFN[v])
{
Tarjan(v);
if(Low[u]>Low[v])
Low[u]=Low[v];
}
else if(Instack[v]&&Low[u]>DFN[v])
Low[u]=DFN[v];
}
if(Low[u]==DFN[u])
{
cnt++;
do{
v=Stack[top--];
Instack[v]=false;
Belong[v]=cnt;
}while(u!=v);
}
return;
}
bool SAT()
{
int i;
memset(Instack,false,sizeof(Instack));
memset(DFN,0,sizeof(DFN));
memset(Low,0,sizeof(Low));
cnt=top=Index=0;
for(i=0;i<2*M;i++)
if(!DFN[i])
Tarjan(i);
for(i=0;i<M;i++)
if(Belong[2*i]==Belong[2*i+1])
return false;
return true;
}
int main()
{
scanf("%d%d",&N,&M);
Build_Map();
if(SAT())
printf("panda is telling the truth...\n");
else
printf("the evil panda is lying again\n");
return 0;
}