bzoj1997 [Hnoi2010]Planar——2-SAT

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1997

神奇的经典2-SAT问题!

对于两个相交的区间,只能一里一外连边,所以可以进行2-SAT问题的建模;

但 m 太大了,可以用一个平面图的定理,m <= 3*n - 6 来缩小范围;

注意特判要等读入结束后再判掉!!!

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int const maxn=2005,maxm=10005;//
int n,m,T,hd[maxn],ct,dfn[maxn],low[maxn],cr,col[maxn];
int tim,l[maxn],r[maxn],a[maxn],sta[maxn],top;
bool vis[maxn];
struct N{
    int to,nxt;
    N(int t=0,int n=0):to(t),nxt(n) {}
}ed[maxn*maxn*2];
void add(int x,int y){ed[++ct]=N(y,hd[x]); hd[x]=ct;}
void tarjan(int x)
{
    dfn[x]=low[x]=++tim;
    sta[++top]=x; vis[x]=1;
    for(int i=hd[x],u;i;i=ed[i].nxt)
    {
        if(!dfn[u=ed[i].to])tarjan(u),low[x]=min(low[x],low[u]);
        else if(vis[u])low[x]=min(low[x],dfn[u]);
    }
    if(low[x]==dfn[x])
    {
        cr++; int y;
        while((y=sta[top])!=x){top--; vis[y]=0; col[y]=cr;}
        top--; vis[x]=0; col[x]=cr;
    }
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)scanf("%d%d",&l[i],&r[i]);
        for(int i=1,x;i<=n;i++)scanf("%d",&x),a[x]=i;
        if(m>3*n-6){printf("NO\n"); continue;}//先读完再判NO!!! 
        int tp=0;
        for(int i=1;i<=m;i++)
        {
            l[i]=a[l[i]],r[i]=a[r[i]];
            if(l[i]>r[i])swap(l[i],r[i]);
//            if(r[i]-l[i]==1||(r[i]==n&&l[i]==1))continue;//没有也可 
//            l[++tp]=l[i],r[tp]=r[i];
        }
//        m=tp;
        ct=0; memset(hd,0,sizeof hd);
        for(int i=1;i<=m;i++)
            for(int j=i+1;j<=m;j++)
            {
                if((l[i]<l[j]&&r[i]<r[j]&&r[i]>l[j])||(l[j]<l[i]&&r[j]<r[i]&&r[j]>l[i]))
                    add(i,j+m),add(j+m,i),add(j,i+m),add(i+m,j);
            }
        tim=0; top=0; cr=0;
        memset(low,0,sizeof low);
//        memset(col,0,sizeof col);
        memset(dfn,0,sizeof dfn);
        for(int i=1;i<=m*2;i++)
            if(!dfn[i])tarjan(i);
        bool fl=0;
        for(int i=1;i<=m;i++)
            if(col[i]==col[i+m]){fl=1; break;}
        if(fl)printf("NO\n");
        else printf("YES\n");
    }
    return 0;
}

 

posted @ 2018-07-08 16:20  Zinn  阅读(150)  评论(0编辑  收藏  举报