Tarjan/2-SAT
Tags:圖論
作業部落
評論地址
Tarjan
用來求割邊或者割點,求點雙聯通分量或者邊雙聯通分量
點雙聯通分量:兩個點之間有兩條點不相交的路徑
邊雙聯通分量:兩個點之間有兩條邊不相交的路徑
Tarjan求LCA還不會
2-SAT
每種物品有選或者不選兩種狀態,有些限制條件形如
選了\(A\)則必須選\(B\),\(A\)和\(B\)不能同時選,必須選\(A\)等等
把邏輯限制關系變成連邊
a->b表示如果\(a\)成立那么\(b\)一定成立
這個要求你理解逆否命題
逆否命題,舉個例子,選\(A\)必須選\(B\),那么我選了\(B'\)就不能選\(A\),選\(B'\)就必須選\(A'\)
由於逆否命題產生的對稱性使得\(2-SAT\)問題得以在\(O(n)\)時間求解
具體來說要求同時連接x->y y'->x'
這樣跑一遍\(Tarjan\)縮點后如果統一物品的兩種狀態在同一個邊雙連通分量中就無解
否則可以輸出方案,具體來說是每個點選擇\(Tarjan\)縮成的超級點中編號最小的那個(也就是反圖拓撲序最小的那個)
題單
Tarjan
- [x] [luogu3387]【模板】縮點 https://www.luogu.org/problemnew/show/P3387
- [x] [luogu3388]【模板】割點(割頂)https://www.luogu.org/problemnew/show/P3388
- [x] [APIO2009]搶掠計划 https://www.luogu.org/problemnew/show/P3627
- [x] [HAOI2006]受歡迎的牛 https://www.luogu.org/problemnew/show/P2341
- [ ] [USACO5.3]校園網 https://www.luogu.org/problemnew/show/P2746
- [ ] [luogu3398]倉鼠找sugar https://www.luogu.org/problemnew/show/P3398
- [x] [HNOI2006]潘多拉的寶盒 https://www.luogu.org/problemnew/show/P2321
- [ ] [HAOI2010]軟件安裝 https://www.luogu.org/problemnew/show/P2515
- [x] [HNOI2012]礦場搭建 https://www.luogu.org/problemnew/show/P3225
- [ ] [SDOI2010]所駝門王的寶藏 https://www.luogu.org/problemnew/show/P2403
2-SAT
- [x] [POI2001]和平委員會 http://cogs.pro:8080/cogs/problem/problem.php?pid=313
- [x] [JSOI2010]滿漢全席 https://www.luogu.org/problemnew/show/P4171
- [x] [UOJ210]尋找罪犯 http://uoj.ac/problem/210
- [x] [NOI2017]游戲 https://www.luogu.org/problemnew/show/P3825
- [ ] [HNOI2010]平面圖判定 https://www.luogu.org/problemnew/show/P3209
- [ ] [POI2010]KOL-Railway https://www.luogu.org/problemnew/show/P3497
Code
邊雙
void Tarjan(int x)
{
vis[x]=1;sta[++top]=x;
dfn[x]=low[x]=++tot;
for(int i=A.head[x],R=A.a[i].to;i;i=A.a[i].next,R=A.a[i].to)
if(!dfn[R]) Tarjan(R),low[x]=min(low[x],low[R]);
else if(vis[R]) low[x]=min(low[x],low[R]);
if(low[x]!=dfn[x]) return;
for(int k=sta[top],lst=0;lst!=x;lst=k,k=sta[--top])
vis[k]=0,bel[k]=x,val[x]+=val[k]*(k!=x);
}
點雙
void Tarjan(int x,int f)
{
int s=0;dfn[x]=low[x]=++tot;
for(int i=head[x];i;i=a[i].next)
{
int R=a[i].to;if(R==f) continue;
if(dfn[R]) {low[x]=min(low[x],dfn[R]);continue;}
s++;Tarjan(R,x);tag[x]|=low[R]>=dfn[x];
low[x]=min(low[x],low[R]);
}
if(!f&&s==1) tag[x]=0;
}
!注意點雙第7行一定要是dfn[R]
2-sat(和平委員會)
#include<cstdio>
#include<cstdlib>
#include<queue>
using namespace std;
const int N=41000;
struct edge {int next,fr,to;};
struct Map
{
edge a[N]; int head[N],cnt;
void link(int x,int y) {a[++cnt]=(edge){head[x],x,y};head[x]=cnt;}
}A,B;
int dfn[N],top,sta[N],vis[N],low[N];
int bel[N],tot,n,m,p[N],node;
queue<int> Q;
void Min(int &a,int b) {if(b<a) a=b;}
void Tarjan(int x)
{
vis[x]=1;sta[++top]=x; dfn[x]=low[x]=++tot;
for(int i=A.head[x],R=A.a[i].to;i;i=A.a[i].next,R=A.a[i].to)
if(!dfn[R]) Tarjan(R),Min(low[x],low[R]);
else if(vis[R]) Min(low[x],low[R]);
if(low[x]!=dfn[x]) return;++node;
for(int k=sta[top],lst=0;lst!=x;lst=k,k=sta[--top])
vis[k]=0,bel[k]=node;
}
int main()
{
// freopen("spo.in","r",stdin);
// freopen("spo.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n*2;i++) p[i]=i&1?i+1:i-1;
for(int i=1;i<=m;i++)
{
int x,y;scanf("%d%d",&x,&y);
A.link(x,p[y]);A.link(y,p[x]);
}
for(int i=1;i<=n*2;i++) if(!dfn[i]) Tarjan(i);
for(int i=1;i<=n*2;i+=2) if(bel[i]==bel[p[i]]) {puts("NIE");exit(0);}
for(int i=1;i<=n*2;i+=2) printf("%d\n",bel[i]<bel[p[i]]?i:p[i]);
}
