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"); }