Time Limit: 1000MS | Memory Limit: 131072K | |
Total Submissions: 8729 | Accepted: 3224 |
Description
liympanda, one of Ikki’s friend, likes playing games with Ikki. Today after minesweeping with Ikki and winning so many times, he is tired of such easy games and wants to play another game with Ikki.
liympanda has a magic circle and he puts it on a plane, there are n points on its boundary in circular border: 0, 1, 2, …, n − 1. Evil panda claims that he is connecting m pairs of points. To connect two points, liympanda either places the link entirely inside the circle or entirely outside the circle. Now liympanda tells Ikki no two links touch inside/outside the circle, except on the boundary. He wants Ikki to figure out whether this is possible…
Despaired at the minesweeping game just played, Ikki is totally at a loss, so he decides to write a program to help him.
Input
The input contains exactly one test case.
In the test case there will be a line consisting of of two integers: n and m (n ≤ 1,000, m ≤ 500). The following m lines each contain two integers ai and bi, which denote the endpoints of the ith wire. Every point will have at most one link.
Output
Output a line, either “panda is telling the truth...
” or “the evil panda is lying again
”.
Sample Input
4 2 0 1 3 2
Sample Output
panda is telling the truth...
刚看2-sat,可能有的地方说的不对,欢迎指正。
题意:给一个圆,在圆上有n个点(从0到n-1),现在给你m条线即a点连到b点(这条线要么全在圆内要么全在圆外)。 要求任意两条线不能相交且每个点最多连一条线,问你存不存在这样的图形。
转化问题:每条边可以在圆外,也可以在圆内,这就满足2-sat要求的两个状态。因此需要按边来处理,我们设Ai为第i条边在圆内,Bi为第i条边在圆外(两者是相对的就像真与假一样)。
思路:我们假设任意两边不能相交,那么对于有可能相交的两条线x和y必有
(Ax 析取 Ay) 合取 (Bx 析取 By) 为真 好好理解. 则该布尔表达式等价于下面四个表达式的合取:
一: Ax -> By 即x在圆内,y在圆外 不会相交
二: Bx -> Ay 即x在圆外,y在圆内 不会相交
三: Ay -> Bx 即y在圆内,y在圆外 不会相交
四: By -> Ax 即y在圆外,x在圆内 不会相交
判断两条线相交的方法,自己在一个圆上画a,b,c,d四个点,分别连接a->b c->d,看相交时满足什么条件。这里我就不说了。
对于任意有可能相交的两边,都要进行以上处理即建边。
我的代码是从编号1开始建图的,把第i条边的Ai 虚拟为i + m,把Bi虚拟成i + 2*m。建好图后就是tarjan有向图求SCC了,
最后枚举所有边i (1 -> m),判断Ai 和 Bi是否在同一个SCC,若满足一个说明存在相交的两边,与假设矛盾即图形不存在。
AC代码:
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <algorithm>
#define MAXN 4000
#define MAXM 500000
#define INF 10000000
using namespace std;
int n, m;
struct Edge
{
int s, t;//起点 终点
}num[MAXM];//存储每条线
vector<int> G[MAXN];
int low[MAXN], dfn[MAXN];
int dfs_clock;
int sccno[MAXN], scc_cnt;
stack<int> S;
bool Instack[MAXN];
void init()
{
for(int i = 1; i <= 3*m; i++) G[i].clear();
}
bool judge(int a, int b, int c, int d)//判断是否相交
{
return (c > a && b > c && d > b) || (a > c && d > a && b > d);
}
void getMap()
{
for(int i = 1; i <= m; i++) scanf("%d%d", &num[i].s, &num[i].t);
/* i + n表内 i +2*n表外*/
for(int i = 1; i <= m; i++)
{
for(int j = 1; j < i; j++)
{
if(judge(num[i].s, num[i].t, num[j].s, num[j].t))//相交
{
G[i + 2*m].push_back(j + m);//i外 -> j内
G[i + m].push_back(j + 2*m);//i内 -> j外
G[j + m].push_back(i + 2*m);//j内 -> i外
G[j + 2*m].push_back(i + m);//j外 -> i内
}
}
}
}
void tarjan(int u, int fa)
{
int v;
low[u] = dfn[u] = ++dfs_clock;
S.push(u);
Instack[u] = true;
for(int i = 0 ;i < G[u].size(); i++)
{
v = G[u][i];
if(!dfn[v])
{
tarjan(v, u);
low[u] = min(low[v], low[u]);
}
else if(Instack[v])
low[u] = min(low[u], dfn[v]);
}
if(low[u] == dfn[u])
{
scc_cnt++;
for(;;)
{
v = S.top(); S.pop();
Instack[v] = false;
sccno[v] = scc_cnt;
if(v == u) break;
}
}
}
void find_cut(int l, int r)//tarjan求SCC
{
memset(low, 0, sizeof(low));
memset(dfn, 0, sizeof(dfn));
memset(sccno, 0, sizeof(sccno));
memset(Instack, false, sizeof(Instack));
dfs_clock = scc_cnt = 0;
for(int i = l; i <= r; i++)
if(!dfn[i]) tarjan(i, -1);
}
void solve()
{
bool flag = true;
for(int i = 1; i <= m; i++)
{
if(sccno[i + m] == sccno[i + 2*m])
{
flag = false;
printf("the evil panda is lying again\n");
break;
}
}
if(flag)
printf("panda is telling the truth...\n");
}
int main()
{
scanf("%d%d", &n, &m);
init();
getMap();
find_cut(1, 3*m);//按边处理 总编号为3*m
solve();
return 0;
}