题目描述
自我翻译提高
有一个环,环上有0~n-1编号的n个点,现在要在点与点之间连m条边,这m条边可以在环里面连,也可以在环外面连
现在给你边的描述,请你判断这些边会不会相交
输入:
第一行两个正整数n(≤1000),m(≤500)
接下来m行每行两个正整数a,b表示边的端点
输出:
若会相交输出“the evil panda is lying again”
否则输出“panda is telling the truth”
分析
首先这个边有两种,很容易想到把边拆成两种
然后判断环内边是否会相交相信大家都会即:
a1>a2>b1>b2
然后环内会相交的(如i1,i2环内相交)肯定表示:
i1号边连里面,i2号边连外面(同样i2连里面i1也连外面)
然后因为双向所以连多一次
跑2-sat就行了
#include <iostream>
#include <cstdio>
#define rep(i,a,b) for (i=a;i<=b;i++)
#define sep(i,start,n) for (i=start;i;i=n[i].next)
using namespace std;
int n,m;
int edgecnt;
struct edge
{
int u,v,next;
};
edge G[5000001];
int list[2001];
int stk[2001],instk[2001],top;
int low[2001],dfn[2001],time;
int idcnt,id[2001];
void ADD(int u,int v)
{
G[++edgecnt].u=u;G[edgecnt].v=v;G[edgecnt].next=list[u];list[u]=edgecnt;return;
}
void TARJAN(int i)
{
int k,j;
stk[++top]=i;instk[i]=1;
low[i]=dfn[i]=++time;
sep(k,list[i],G)
{
j=G[k].v;
if (!dfn[j])
{
TARJAN(j);
low[i]=min(low[i],low[j]);
}
else
if (instk[j])
low[i]=min(low[i],dfn[j]);
}
if (low[i]==dfn[i])
{
idcnt++;
int nowstk;
do
{
nowstk=stk[top--];
instk[nowstk]=0;
id[nowstk]=idcnt;
}
while (nowstk!=i);
}
return;
}
void INIT()
{
int i,j,a[501],b[501];
scanf("%d%d",&n,&m);
rep(i,1,m)
{
scanf("%d%d",&a[i],&b[i]);
if (a[i]<b[i]) swap(a[i],b[i]);
}
rep(i,1,m-1)
rep(j,i+1,m)
if (a[j]>a[i]&&a[i]>b[j]&&b[j]>b[i]||a[i]>a[j]&&a[j]>b[i]&&b[i]>b[j])
{
ADD(i,j+m);
ADD(i+m,j);
ADD(j+m,i);
ADD(j,i+m);
}
return;
}
void DOIT()
{
int i;
rep(i,1,2*m)
if (!dfn[i]) TARJAN(i);
rep(i,1,m)
if (id[i]==id[i+m])
{
printf("the evil panda is lying again");
return;
}
printf("panda is telling the truth...");
return;
}
int main()
{
INIT();
DOIT();
return 0;
}