2-sat

P3209 [HNOI2010]平面圖斷定

題目:

若能將無向圖 $G=(V, E)$ 畫在平面上使得任意兩條無重合頂點的邊不相交,則稱 $G$ 是平面圖。斷定一個圖是否爲平面圖的問題是圖論中的一個重要問題。如今假設你要斷定的是一類特殊的圖,圖中存在一個包含全部頂點的環,即存在哈密頓迴路。html

思路:

1.2-satnode

關於平面圖有一個性質,邊數必定小於等於 $3n-6$ 。因此咱們對於邊數大的直接獲得答案。其他的咱們 $O(n^{2})$ 判斷兩條連邊是否矛盾,把每條邊分離成兩個點表示邊連在外部仍是內部。若是矛盾就連邊。c++

2.並查集git

在同樣的性質下,與 $i+n$ 相連的點表示不能與 $i$ 在同一側,因此對於每對矛盾的邊 $fa[getfa(x)]=getfa(y+n),fa[getfa(y)]=getfa(x+n)$ 。ide

若是若是有一對矛盾的點屬於同一個集合,則不合法。spa

代碼:

我寫的是第二種啊,感受代碼比較短...code

#include<bits/stdc++.h>
#define il inline
#define _(d) while(d(isdigit(ch=getchar())))
using namespace std;
const int N=1e4+5,M=1e4+5;
int n,m,v[N],l[N],tot,id[N],fa[N];
struct node{
    int x,y;
}t[M];
il int read(){
   int x,f=1;char ch;
   _(!)ch=='-'?f=-1:f;x=ch^48;
   _()x=(x<<1)+(x<<3)+(ch^48);
   return f*x;
}
il int getfa(int x){
    if(fa[x]==x)return x;return fa[x]=getfa(fa[x]);
}
il bool cross(int u1,int v1,int u2,int v2){
    u1=id[u1];v1=id[v1];u2=id[u2];v2=id[v2];
    if(u1>v1)swap(u1,v1);if(u2>v2)swap(u2,v2);
    return ((u1<u2&&u2<v1&&v1<v2)||(u2<u1&&u1<v2&&v2<v1));
}
int main()
{
    int T=read();while(T--){
        n=read();m=read();
        for(int i=1;i<=m;i++)t[i].x=read(),t[i].y=read();
        for(int i=1;i<=n;i++)id[v[i]=read()]=i;
        if(m>3*n-6){puts("NO");continue;}
        for(int i=2;i<=n;i++)l[v[i]]=v[i-1];l[v[1]]=v[n];
        tot=0;
        for(int i=1;i<=m;i++)
            if(l[t[i].x]!=t[i].y&&l[t[i].y]!=t[i].x)t[++tot]=t[i];
        m=tot;
        for(int i=1;i<=(m<<1);i++)fa[i]=i;bool pd=0;
        for(int i=1;i<=m;i++)for(int j=i+1;j<=m;j++){
            if(!cross(t[i].x,t[i].y,t[j].x,t[j].y))continue;
            int x=getfa(i),y=getfa(j);
            if(x==y){pd=1;break;}
            fa[x]=getfa(j+m);fa[y]=getfa(i+m);
        }
        if(pd)puts("NO");else puts("YES");
    }
    return 0;
}
View Code

 

dtoj#3858. 遊戲

題目:

小 L 計劃進行 $ n $ 場遊戲,每場遊戲使用一張地圖,小 L 會選擇一輛車在該地圖上完成遊戲。小 L 的賽車有三輛,分別用大寫字母A、B、C表示。htm

地圖一共有四種,分別用小寫字母x、a、b、c表示。blog

其中,賽車A不適合在地圖a上使用,賽車B不適合在地圖b上使用,賽車C不適合在地圖c上使用,而地圖x則適合全部賽車參加。遊戲

適合全部賽車參加的地圖並很少見,最多隻會有 $ d $ 張。

$ n $ 場遊戲的地圖能夠用一個小寫字母組成的字符串描述。例如: $ S=xaabxcbc $ 表示小L計劃進行 $ 8 $ 場遊戲,其中第 $ 1 $ 場和第 $ 5 $ 場的地圖類型是x,適合全部賽車,第2場和第3場的地圖是a,不適合賽車A,第4場和第7場的地圖是b,不適合賽車B,第6場和第8場的地圖是c,不適合賽車C。

小 L 對遊戲有一些特殊的要求,這些要求能夠用四元組 $ (i,h_i,j,h_j) $ 來描述,表示若在第 $ i $ 場使用型號爲 $ h_i $ 的車子,則第 $ j $ 場遊戲要使用型號爲 $ h_j $ 的車子。

你能幫小 L 選擇每場遊戲使用的賽車嗎?若是有多種方案,輸出任意一種方案。

若是無解,輸出 $ -1 $ 。

 思路:

當 $d=0$ 時對於每場比賽有兩種選擇,因而想到 $2-sat$ 。

考慮對於限制

若是 $i$ 場比賽不能放 $h_{i}$ ,那就忽略這個限制。

若是第 $j$ 場不能放 $h_{j}$ ,那就意味着不能在 $i$ 場放 $h_{i}$ 那就鏈接 $(放 $h_{i}$,不放 $h_{i}$ )。

其餘狀況就是鏈接 (放 $h_{i}$ ,放 $h_{j}$ ),(不放 $h_{j}$ ,不放 $h_{i}$ )。

代碼:

#include<bits/stdc++.h>
#define il inline
#define _(d) while(d(isdigit(ch=getchar())))
using namespace std;
const int N=1e5+5;
char s[N];bool vis[N];
int sta[N],top,tot,col[N],c;
int n,d,m,pos[N],cnt,head[N],ne[N<<1],to[N<<1],dfn[N],low[N];
struct node{
    int x,y;char hx,hy;
}t[N];
il int read(){
   int x,f=1;char ch;
   _(!)ch=='-'?f=-1:f;x=ch^48;
   _()x=(x<<1)+(x<<3)+(ch^48);
   return f*x;
}
il char getc(){
    char ch='0';
    while(ch!='A'&&ch!='B'&&ch!='C')ch=getchar();
    return ch;
}
il void ins(int x,int y){
    ne[++cnt]=head[x];head[x]=cnt;to[cnt]=y;
}
il int id(int x,char ch,int op){
    if(op==1){
        if(s[x]=='a'){
            if(ch=='B')return x;return x+n;
        }
        if(s[x]=='b'){
            if(ch=='A')return x;return x+n;
        }
        if(s[x]=='c'){
            if(ch=='A')return x;return x+n;
        }
    }
    else{
        if(s[x]=='a'){
            if(ch=='B')return x+n;return x;
        }
        if(s[x]=='b'){
            if(ch=='A')return x+n;return x;
        }
        if(s[x]=='c'){
            if(ch=='A')return x+n;return x;
        }
    }
}
il void prin(int p,int op){
//    printf("!!%d %d \n",b)
    if(s[p]=='a'){
        if(op==0)printf("B");else printf("C");
    }
    if(s[p]=='b'){
        if(op==0)printf("A");else printf("C");
    }
    if(s[p]=='c'){
        if(op==0)printf("A");else printf("B");
    }
}
il void tarjan(int x){
    dfn[x]=low[x]=++tot;sta[++top]=x;vis[x]=1;
    for(int i=head[x];i;i=ne[i]){
        if(!dfn[to[i]])tarjan(to[i]),low[x]=min(low[x],low[to[i]]);
        else if(vis[to[i]])low[x]=min(low[x],dfn[to[i]]);
    }
    if(dfn[x]==low[x]){
        vis[x]=0;col[x]=++c;
        while(sta[top]!=x)vis[sta[top]]=0,col[sta[top--]]=c;
        top--;
    }
}
il bool pd(){
    cnt=0;
    for(int i=1;i<=(n<<1);i++)head[i]=dfn[i]=0;cnt=c=tot=0;
    for(int i=1;i<=m;i++){
        if(t[i].hx+32==s[t[i].x])continue;
        if(t[i].hy+32==s[t[i].y]){
            ins(id(t[i].x,t[i].hx,1),id(t[i].x,t[i].hx,0));
            continue;
        }
        ins(id(t[i].x,t[i].hx,1),id(t[i].y,t[i].hy,1));
        ins(id(t[i].y,t[i].hy,0),id(t[i].x,t[i].hx,0));
    }
    for(int i=1;i<=(n<<1);i++)if(!dfn[i])tarjan(i);
    for(int i=1;i<=n;i++)if(col[i]==col[i+n])return 0;
    for(int i=1;i<=n;i++){
        if(col[i]<col[i+n])prin(i,0);
        else prin(i,1);
    }
    return 1;
}
il void dfs(int x){
    if(x>d){
        if(pd())exit(0);
        return;
    }
    s[pos[x]]='a';dfs(x+1);
    s[pos[x]]='b';dfs(x+1);
}
int main()
{
    n=read();read();
    scanf(" %s",s+1);
    for(int i=1;i<=n;i++)if(s[i]=='x')pos[++d]=i;
    m=read();
    for(int i=1;i<=m;i++){
        t[i].x=read();t[i].hx=getc();
        t[i].y=read();t[i].hy=getc();
    }
    dfs(1);puts("-1");
    return 0;
}
View Code

 

bzoj3495 PA2010 Riddle

題目:

有 $n$ 個城鎮被分紅了 $k$ 個郡,有 $m$ 條鏈接城鎮的無向邊。
要求給每一個郡選擇一個城鎮做爲首都,知足每條邊至少有一個端點是首都。

思路:

有一個前綴建圖的思路,對於每個城鎮去一個前綴和,由於一個城鎮只能有一個首都,因此前綴和取值爲 $0/1$ 。
因而有如下幾種關係(前爲條件後爲結果):
1. $a[i]=1,s[i]=1$ 
2. $s[i]=0,a[i]=1$ 
3. $a[i]=1,s[i-1]=0$ 
4. $s[i-1]=1,a[i]=0$ 
5. $s[i-1]=1,s[i]=1$
6. $s[i]=0,s[i-1]=0$

代碼:

#include<bits/stdc++.h>
#define il inline
#define _(d) while(d(isdigit(ch=getchar())))
using namespace std;
const int N=4e6+5;
bool vis[N];
int col[N],sta[N],top,c;
int n,m,k,head[N],ne[N<<1],to[N<<1],cnt,dfn[N],low[N],tot;
il int read(){
   int x,f=1;char ch;
   _(!)ch=='-'?f=-1:f;x=ch^48;
   _()x=(x<<1)+(x<<3)+(ch^48);
   return f*x;
}
il void ins(int x,int y){
    ne[++cnt]=head[x];head[x]=cnt;to[cnt]=y;
}
il void tarjan(int x){
    dfn[x]=low[x]=++tot;sta[++top]=x;vis[x]=1;
    for(int i=head[x];i;i=ne[i]){
        if(!dfn[to[i]])tarjan(to[i]),low[x]=min(low[x],low[to[i]]);
        else if(vis[to[i]])low[x]=min(low[x],dfn[to[i]]);
    }
    if(dfn[x]==low[x]){
        vis[x]=0;col[x]=++c;
        while(sta[top]!=x)vis[sta[top]]=0,col[sta[top--]]=c;
        top--;
    }
}
il bool pd(){
    for(int i=1;i<=n;i++)if(col[i]==col[i+n])return 0;
    for(int i=n*2+1;i<=n*3;i++)if(col[i]==col[i+n])return 0;
    return 1;
}
int main()
{
    n=read();m=read();k=read();
    for(int i=1;i<=m;i++){
        int x=read(),y=read();
        ins(x+n,y);ins(y+n,x);
    }
    for(int i=1;i<=n;i++)ins(i,i+n*2),ins(i+n*3,i+n);
    for(int i=1;i<=k;i++){
        int d=read(),las=read();
        for(int j=2;j<=d;j++){
            int now=read();
            ins(now,las+n*3);ins(las+n*2,now+n);
            ins(las+n*2,now+n*2);ins(now+n*3,las+n*3);las=now;
        }
    }
    for(int i=1;i<=(n<<2);i++)if(!dfn[i])tarjan(i);
    if(pd())puts("TAK");else puts("NIE");
    return 0;
}
View Code
本站公眾號
   歡迎關注本站公眾號,獲取更多信息