题目传送门:【POJ 3207】
题目大意: Liympanda 有一个魔法圆环,他把这个圆环放在一个平面上。圆环上有 n 个点:0,1,2,…,n-1。Evil panda 声称他要连接圆环上的 m 对点。为了连接两个点,Liympanda 需要把这条连线整个地放在圆的内部,或者是放在圆的外部。Liympanda 告诉你,任意两条(圆外和圆内的)连线都不能相交,每个点都最多只会连上一条线。现在他想让你帮助他回答这个问题。
输入恰好为一组数据。第一行两个整数 n , m(n ≤ 1000 , m ≤ 500),代表有 n 个点和 m 条连线。接下来 m 行,每行两个数 ai , bi ,表示第 i <script type="math/tex" id="MathJax-Element-570">i</script> 条线连接的两个点。如果连线两两不相交,那么输出“panda is telling the truth…”;否则输出“the evil panda is lying again”。
题目分析:
由题,对于每条连线,都只存在两种状态:这条线在圆内,或者是这条线在圆外;同时,每一条线只能选择其中的一种状态。因此,我们可以将每一条线转化为两个点,分别表示“圆内”和“圆外”。那么很显然,我们可以使用 2-SAT 来做。
对于一些情况,当两条线互相包夹的时候(例如,第 1 条线连的 1,4,第 2 条线连的 2,5),此时这两条线必须为一条在圆内,另一条在圆外,否则就会相交。所以对于这种情况,我们可以按照 2-SAT 的方式去连边来表示它们的冲突情况(1内->2外,2内->1外,等等)。然后跑一遍 Tarjan 看有没有既为圆内又为圆外的点即可。
对于上面的样例,如果还存在第 3 条线(3,6),那么此时一定会相交,因为跑完 Tarjan 后的图就有“既为圆内又为圆外的点”的情况。
下面附上代码:
- #include<cstdio>
- #include<cstring>
- #include<iostream>
- #include<algorithm>
- #include<stack>
- using namespace std;
- const int MX = 1005;
- struct Edge{
- int to,next;
- }edge[MX * MX * 2];
- struct Point{ //表示每一对点的编号
- int a,b;
- }pt[MX];
- int n,m;
- int head[MX],now = 0,cnt = 0,_index = 0,dfn[MX],low[MX],belong[MX];
- bool ins[MX];
- stack<int> s;
- void adde(int u,int v){
- edge[++now].to = v;
- edge[now].next = head[u];
- head[u] = now;
- }
- void tarjan(int u){
- dfn[u] = low[u] = ++_index;
- s.push(u);
- ins[u] = true;
- for (int i = head[u];i;i = edge[i].next){
- int v = edge[i].to;
- if (!dfn[v]){
- tarjan(v);
- if (low[v] < low[u]){
- low[u] = low[v];
- }
- } else if (ins[v] && dfn[v] < low[u]){
- low[u] = dfn[v];
- }
- }
- if (low[u] == dfn[u]){
- ++cnt;
- int tmp;
- do {
- tmp = s.top();
- s.pop();
- ins[tmp] = false;
- belong[tmp] = cnt;
- }while (tmp != u);
- }
- }
- int main(){
- int a,b;
- cin>>n>>m;
- for (int i = 1;i <= m;i++){
- cin>>a>>b;
- ++a;++b; //swap和+1,方便之后处理
- if (a>b) swap(a,b);
- pt[i].a = a;
- pt[i].b = b;
- }
- for (int i = 1;i <= m;i++){
- for (int j = i + 1;j <= m;j++){
- if ((pt[i].a < pt[j].a && pt[i].b > pt[j].a && pt[i].b < pt[j].b)
- || (pt[i].a > pt[j].a && pt[i].a < pt[j].b && pt[i].b > pt[j].b)){
- //如果两个点互相包夹(如上),说明它们必须为一条边在圆内,一条边在圆外
- //按照2-SAT的对应关系建边
- adde(i,j + m);
- adde(j,i + m);
- adde(i + m,j);
- adde(j + m,i);
- }
- }
- }
- for (int i = 1;i <= 2 * m;i++){
- if (!dfn[i]) tarjan(i);
- }
- bool reach = true;
- for (int i = 1;i <= m;i++){
- if (belong[i] == belong[i + m]){ //表示这条边既在圆内,又在圆外,矛盾
- reach = false;
- break;
- }
- }
- if (reach) printf(“panda is telling the truth…”);
- else printf(“the evil panda is lying again”);
- return 0;
- }