难度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 ' ' #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"); }