1. 程式人生 > 實用技巧 >Codeforces 776D【The Door Problem】(2-SAT)

Codeforces 776D【The Door Problem】(2-SAT)

難度CF2000分

題意:給你n個門,m把鎖。每個門由2把鎖控制,給你每個門的初始狀態以及每把鎖可以控制的t個門,問你能否使用鎖將這些門全部開啟。0是關閉,1是開啟。

題解:因為很久沒使用2-sat導致自己一開始不會做這個題,然後看了題解才恍然大悟。我們這麼來想:因為每個門只能被2把鑰匙控制,如果門初始的狀態是0,那麼也就是控制該門的2個鑰匙只有1個鑰匙生效;如果門的狀態是1,那麼就是控制該門的2個鑰匙都生效或者都不生效。那麼我們就可以將其轉化為2-sat模型。生效為2*i,不生效為2*i+1。然後進行建邊操作,最後跑tarjan看是否有id[2*i]==id[2*i+1]的情況,如果有的話說明不行,輸出NO,反之輸出YES

#include<bits/stdc++.h>
#pragma GCC optimize(2)
#define ll long long
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,n,a) for(int i=n;i>=a;i--)
#define endl '\n'
#define eps 0.000000001
#define pb push_back
#define mem(a,b) memset(a,b,sizeof(a))
#define IO ios::sync_with_stdio(false);cin.tie(0);
using
namespace std; const int INF=0x3f3f3f3f; const ll inf=0x3f3f3f3f3f3f3f3f; const int mod=1e9+7; const int maxn=2e5+5; int tot,head[maxn]; struct E{ int to,next; }edge[maxn<<1]; void add(int u,int v){ edge[tot].to=v; edge[tot].next=head[u]; head[u]=tot++; } int dfn[maxn],low[maxn],id[maxn],vis[maxn],cnt,tott; stack
<int> s; void tarjan(int x){ dfn[x]=low[x]=++tott; s.push(x);vis[x]=1; for(int i=head[x];i!=-1;i=edge[i].next){ int v=edge[i].to; if(!dfn[v]){ tarjan(v); low[x]=min(low[x],low[v]); } else if(vis[v]){ low[x]=min(low[x],dfn[v]); } } if(low[x]==dfn[x]){ ++cnt; while(1){ int now=s.top();s.pop(); id[now]=cnt; vis[now]=0; if(x==now) break; } } } int n,m,a[maxn]; vector<int> w[maxn]; int main(){ scanf("%d%d",&n,&m);mem(head,-1); rep(i,1,n) scanf("%d",&a[i]); rep(i,1,m){ int t;scanf("%d",&t); while(t--){ int x;scanf("%d",&x); w[x].push_back(i); } } rep(i,1,n){ int x=w[i][0],y=w[i][1]; x*=2;y*=2; if(a[i]==0){ add(x,y+1);add(y+1,x); add(x+1,y);add(y,x+1); }else{ add(x,y);add(y,x); add(x+1,y+1);add(y+1,x+1); } } rep(i,2,2*m+1){ if(!dfn[i]) tarjan(i); } for(int i=1;i<=m;i++){ if(id[i*2]==id[i*2+1]){ puts("NO");return 0; } } puts("YES"); }
View Code