题目链接
http://poj.org/problem?id=3207
思路
将每条边ii拆成两个点Ai,BiAi,Bi,选AiAi代表这条边在圆外,选BiBi这条边代表这条边在圆内。
如果两条边同时在圆内的话会相交,则连接Ai↔Bj,Aj↔BiAi↔Bj,Aj↔Bi这两条双向边,最后2-sat判是否存在可行解。
代码
#include <cstdio>
#include <algorithm>
const int maxn=1000;
const int maxm=4000000;
int read()
{
int x=0,f=1;
char ch=getchar();
while((ch<'0')||(ch>'9'))
{
if(ch=='-')
{
f=-f;
}
ch=getchar();
}
while((ch>='0')&&(ch<='9'))
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
int pre[maxm+10],now[maxn+10],son[maxm+10],tot;
int dfn[maxn+10],low[maxn+10],belong[maxn+10],cnt,cntl;
int stack[maxn+10],top,instack[maxn+10],n,m;
inline int ins(int a,int b)
{
pre[++tot]=now[a];
now[a]=tot;
son[tot]=b;
return 0;
}
int tarjan(int u)
{
dfn[u]=low[u]=++cnt;
stack[++top]=u;
instack[u]=1;
int j=now[u];
while(j)
{
int v=son[j];
if(dfn[v]==0)
{
tarjan(v);
low[u]=std::min(low[u],low[v]);
}
else if(instack[v])
{
low[u]=std::min(low[u],dfn[v]);
}
j=pre[j];
}
if(dfn[u]==low[u])
{
++cntl;
int w=0;
while(w!=u)
{
w=stack[top--];
belong[w]=cntl;
instack[w]=0;
}
}
return 0;
}
int u[maxn+10],v[maxn+10];
int main()
{
n=read();
m=read();
for(int i=1; i<=m; ++i)
{
u[i]=read();
v[i]=read();
if(u[i]>v[i])
{
std::swap(u[i],v[i]);
}
}
for(int i=1; i<=m; ++i)
{
for(int j=1; j<=m; ++j)
{
if((u[i]<u[j])&&(v[i]<v[j])&&(v[i]>u[j]))
{
ins(i*2,j*2-1);
ins(j*2-1,i*2);
ins(i*2-1,j*2);
ins(j*2,i*2-1);
}
}
}
for(int i=1; i<=2*m; ++i)
{
if(!dfn[i])
{
tarjan(i);
}
}
for(int i=1; i<=m; ++i)
{
if(belong[2*i-1]==belong[2*i])
{
puts("the evil panda is lying again");
return 0;
}
}
puts("panda is telling the truth...");
return 0;
}