P6378 [PA2010] Riddle
思路:
两端点至少选一个点作为关键点的问题用2-SAT进行连边处理即可
本题的难点在于如何维护第二个条件
朴素建边显然是不行的,我们用设置前缀节点$pre_i$的方法优化建边
$pre_i$ 表示在x组中的前i个数已经存在关键点$pre_i'$表示在x组中的前i个数不存在关键点
我们连接$(a,pre_i),(pre_{i-1},pre_{i}).(pre_{i-1},a')$三条边即可对图达成约束
如果存在某一个集合内不存在关键点的情况,那么将其任意一点设为关键点即可
#include<bits/stdc++.h> #define maxn 8000005 #define maxm 500005 #define inf 0x7fffffff #define ll long long #define rint register int #define debug(x) cerr<<#x<<": "<<x<<endl #define fgx cerr<<"--------------"<<endl #define rep(i,a,b) for(int i=a;i<=b;i++) #define dgx cerr<<"=============="<<endl #define lowbit(x) (x&(-x)) #define MAXN 4000003 using namespace std; inline int read(){ int x=0,f=1; char ch=getchar(); while('0'>ch || ch>'9'){if(ch=='-') f=-1; ch=getchar();} while('0'<=ch && ch<='9'){x=(x<<1)+(x<<3)+ch-'0'; ch=getchar();} return x*f; } int n,m,q,pre1[MAXN],pre2[MAXN],to[maxn],nxt[maxn],fir[MAXN],tot,cnt,a[MAXN],dfn[MAXN],low[MAXN],sta[MAXN],top,num,col[MAXN],cc; void ade(int x,int y){ // cout<<x<<"-->"<<y<<endl; to[++tot]=y; nxt[tot]=fir[x]; fir[x]=tot; } void Tarjan(int x){ dfn[x]=low[x]=++num; sta[++top]=x; for(int k=fir[x];k;k=nxt[k]){ if(!dfn[to[k]]){ Tarjan(to[k]); low[x]=min(low[x],low[to[k]]); } else if(!col[to[k]]) low[x]=min(low[x],dfn[to[k]]); } // cout<<x<<" "<<low[x]<<" "<<dfn[x]<<endl; if(low[x]==dfn[x]){++cc;while(sta[top+1]!=x) col[sta[top]]=cc,top--;} } int main(){ n=read(); m=read(); q=read(); rep(i,1,m){ int x=read(),y=read(); ade(x+n,y); ade(y+n,x); }cnt=n+n; rep(i,1,n) pre1[i]=++cnt,pre2[i]=cnt+n; rep(i,1,q){ int s=read(); rep(j,1,s){ a[j]=read(); ade(a[j],pre1[a[j]]); ade(pre2[a[j]],a[j]+n); if(j==1) continue; ade(pre1[a[j-1]],pre1[a[j]]) ; ade(pre2[a[j]],pre2[a[j-1]]); ade(pre1[a[j-1]],a[j]+n); ade(a[j],pre2[a[j-1]]); } } rep(i,1,4*n) if(!dfn[i]) Tarjan(i); // rep(i,1,4*n) cout<<col[i]<<" ";cout<<endl; rep(i,1,n) if(col[i+n]==col[i]){cout<<"NIE"; return 0;} rep(i,2*n+1,3*n) if(col[i+n]==col[i]){cout<<"NIE";return 0;} cout<<"TAK"; return 0; }