P6378 题解
题意简述
给出一张
题目分析
很明显是 2-SAT 问题。如一般 2-SAT 题相同,我们设
那么我们对每条边
对于每“部分”点内的条件转化,首先可以发现每个“部分”至少一个黑点这一条件是无用的。因为若该部分内有至少两个点之间有连边,那么就至少有一个黑点;否则任意两个点之间都没有连边,随便找一个点定为黑点就行。而每个“部分”内只有至多一个黑点一条件,直接处理是将“部分”中每个点
事实上,对于这种类似于对一个图中某点集内的点互相之间连边的操作,可以考虑前缀优化建图的方式解决。例如本题,以
这样对一个点暴力建边的复杂度为
然后我们发现只需要从
对“部分”中结尾一段,也可以类似地采用后缀方式优化。
这样,我们就可以在
代码实现
#include<bits/stdc++.h> using namespace std; int n,m,k,w,x,y; int tot,hd[4000010],v[8000010],nt[8000010]; int cnt,dfn[4000010],low[4000010],stk[4000010]/*栈*/,top,num,c[4000010]/*点所在 SCC 编号*/; bool ins[4000010];//是否入栈 //用下标为 x 的点代表 x_0,x+n 代表 x_1,x+2*n 代表 x_2,x+3*n 代表 x_3 void rd(int &x) { x=0; char c=getchar(); for(;c>'9'||c<'0';c=getchar()); for(;c<='9'&&c>='0';c=getchar()) x=(x<<3)+(x<<1)+c-'0'; }//10^6 的数据,用个快读 void add(int x,int y) { v[++tot]=y; nt[tot]=hd[x]; hd[x]=tot; }//建边 void tarjan(int x)//tarjan 求 SCC { dfn[x]=low[x]=++cnt; stk[++top]=x; ins[x]=1; for(int i=hd[x];i;i=nt[i]) { int y=v[i]; if(!dfn[y]) { tarjan(y); low[x]=min(low[x],low[y]); } else if(ins[y]) low[x]=min(low[x],dfn[y]); } if(dfn[x]==low[x]) { int y; num++; do { y=stk[top--]; ins[y]=0; c[y]=num; }while(y!=x); } } int main() { rd(n),rd(m),rd(k); for(int i=1;i<=m;i++) { rd(x),rd(y); add(x,y+n); add(y,x+n); } for(int i=1;i<=k;i++) { rd(w); y=0;//上一个点 for(int j=1;j<=w;j++) { rd(x); add(x+2*n,x); add(x+3*n,x); if(y) { add(x+n,y+2*n); add(y+n,x+3*n); add(x+2*n,y+2*n); add(y+3*n,x+3*n); } y=x; } } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); for(int i=1;i<=n;i++) if(c[i]==c[i+n])//如果 i_0 和 i_1 在同一个 SCC 里面,推出矛盾! { printf("NIE"); return 0; } printf("TAK"); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!