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

视频链接:D43 2-SAT+前缀优化 P6378 [PA2010] Riddle_哔哩哔哩_bilibili

 

 

 

P6378 [PA2010] Riddle - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

// 2-SAT+前缀优化 O(n+m)
#include <iostream>
#include <cstring>
#include <algorithm>
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");
}

 

// 2-SAT+前缀优化 O(n+m)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

#define N 8000010
int n,m,k,w;
int x0,x1,p0,p1,pp0,pp1,num;
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(x+n,y); //不选x 则选y
    add(y+n,x); //不选y 则选x
  }
  num=2*n;
  for(int i=1;i<=k;++i){ //前缀优化
    scanf("%d",&w);
    for(int j=1;j<=w;++j){
      scanf("%d",&x0);
      x1=x0+n; p0=++num; p1=++num;
      add(x0,p0); //向下连
      add(p1,x1); //向下连
      if(j!=1){
        add(pp0,p0); //向右连
        add(p1,pp1); //向左连
        add(pp0,x1); //向右下连
        add(x0,pp1); //向左下连
      }
      pp0=p0,pp1=p1; //前一个节点
    }
  }
  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");
}

 

posted @ 2024-08-14 19:20  董晓  阅读(69)  评论(0编辑  收藏  举报