1 条题解

  • 0
    @ 2026-1-29 20:31:12

    D43 2-SAT+前缀优化 P6378 [PA2010] Riddle

    // 2-SAT+前缀优化 O(n+m)
    #include<bits/stdc++.h>
    using namespace std;
    
    #define x0(x) x     //点
    #define x1(x) x+n   //反点
    #define p0(x) x+2*n //前缀点
    #define p1(x) x+3*n //前缀反点
    #define N 8000010
    int n,m,k,w;
    int dfn[N],low[N],scc[N],stk[N],tim,top,cnt;
    int head[N],to[N],ne[N],idx;
    
    void add(int a,int b){
      to[++idx]=b;
      ne[idx]=head[a];
      head[a]=idx;
    }
    void tarjan(int x){
      dfn[x]=low[x]=++tim;
      stk[++top]=x;
      for(int i=head[x];i;i=ne[i]){
        int y=to[i];
        if(!dfn[y]){ //若y尚未访问
          tarjan(y);
          low[x]=min(low[x],low[y]);
        }
        else if(!scc[y]) //若y已访问且未处理
          low[x]=min(low[x],dfn[y]);
      }
      
      if(low[x]==dfn[x]){ //若x是SCC的根
        ++cnt;
        for(int y=-1;y!=x;)
          scc[y=stk[top--]]=cnt;
      }
    }
    int main(){
      scanf("%d%d%d",&n,&m,&k);
      for(int i=1,x,y;i<=m;++i){
        scanf("%d%d",&x,&y);
        add(x1(x),x0(y)); //不选x 则选y
        add(x1(y),x0(x)); //不选y 则选x
      }
      for(int i=1;i<=k;++i){ //前缀优化
        scanf("%d",&w);
        for(int j=1,x,p;j<=w;++j){
          scanf("%d",&x);
          add(x0(x),p0(x)); //向下连
          add(p1(x),x1(x)); //向下连
          if(j!=1){
            add(p0(p),p0(x)); //向右连
            add(p1(x),p1(p)); //向左连
            add(p0(p),x1(x)); //向右下连
            add(x0(x),p1(p)); //向左下连
          }
          p=x; //记录前一个x
        }
      }
      for(int i=1;i<=4*n;++i)if(!dfn[i])tarjan(i);
      for(int i=1;i<=n;++i)
        if(scc[i]==scc[i+n]) return puts("NIE"),0;
      puts("TAK");
    }
    
    • 1

    信息

    ID
    5160
    时间
    3000ms
    内存
    512MiB
    难度
    10
    标签
    递交数
    5
    已通过
    3
    上传者