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;
}
原文地址:https://www.cnblogs.com/handsome-zlk/p/14467398.html