题目: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 "); 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 "); else printf("YES "); } return 0; }