1. 程式人生 > >P4782 【模板】2-SAT 問題

P4782 【模板】2-SAT 問題

git inline == true pan www. ssi 排序 fine

傳送門

2-SAT的板子

把每一個點拆成選0或選1

條件為$x_i$為$a$或$x_j$為$b$,那麽如果$x_i$不為$a$則$x_j$必為$b$,同理$x_j$不為$b$則$x_i$必為$a$

那麽從$x_i$不為$a$的點向$x_j$為$b$的點連邊,從$x_j$不為$b$的點向$x_i$為$a$的點連邊

然後跑一個tarjan縮點,在新的DAG的反圖上跑一遍拓撲排序,如果有一個點的兩個取值在同一個強連通分量裏就無解,否則取兩個取值中拓撲序小的那一個

有一個小技巧就是tarjan縮點時的編號實際就是新的DAG的反圖拓撲序,那麽只要看兩個取值誰的拓撲序更小取誰就行了

 1
//minamoto 2 #include<iostream> 3 #include<cstdio> 4 using namespace std; 5 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) 6 char buf[1<<21],*p1=buf,*p2=buf; 7 template<class T>inline bool cmin(T&a,const T&b){return
a>b?a=b,1:0;} 8 inline int read(){ 9 #define num ch-‘0‘ 10 char ch;bool flag=0;int res; 11 while(!isdigit(ch=getc())) 12 (ch==-)&&(flag=true); 13 for(res=num;isdigit(ch=getc());res=res*10+num); 14 (flag)&&(res=-res); 15 #undef num 16 return
res; 17 } 18 char sr[1<<21],z[20];int C=-1,Z; 19 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;} 20 inline void print(int x){ 21 if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x; 22 while(z[++Z]=x%10+48,x/=10); 23 while(sr[++C]=z[Z],--Z);sr[++C]= ; 24 } 25 const int N=2e6+5; 26 int head[N],Next[N],ver[N],tot; 27 inline void add(int u,int v){ 28 ver[++tot]=v,Next[tot]=head[u],head[u]=tot; 29 } 30 int dfn[N],bl[N],low[N],st[N],top,num,cnt,n,m; 31 void tarjan(int u){ 32 dfn[u]=low[u]=++num,st[++top]=u; 33 for(int i=head[u];i;i=Next[i]){ 34 int v=ver[i]; 35 if(!dfn[v]) tarjan(v),cmin(low[u],low[v]); 36 else if(!bl[v]) cmin(low[u],dfn[v]); 37 } 38 if(low[u]==dfn[u]) for(++cnt;st[top+1]!=u;--top) bl[st[top]]=cnt; 39 } 40 int main(){ 41 // freopen("testdata.in","r",stdin); 42 n=read(),m=read(); 43 for(int i=1;i<=m;++i){ 44 int a=read(),b=read(),c=read(),d=read(); 45 add(a+(!b)*n,c+d*n),add(c+(!d)*n,a+b*n); 46 } 47 for(int i=1,l=n<<1;i<=l;++i) if(!dfn[i]) tarjan(i); 48 for(int i=1;i<=n;++i) 49 if(bl[i]==bl[i+n]) return puts("IMPOSSIBLE"),0; 50 puts("POSSIBLE"); 51 for(int i=1;i<=n;++i) print(bl[i+n]<bl[i]); 52 Ot(); 53 return 0; 54 }

P4782 【模板】2-SAT 問題