UOJ210【UER #6】寻找罪犯 (2-SAT前后缀优化建边)
来源:互联网 发布:韩美林 知乎 编辑:程序博客网 时间:2024/06/07 20:17
UOJ210【UER #6】寻找罪犯
原题地址:http://uoj.ac/problem/210
题意:
n 个嫌疑人,编号为 1 到 n,严刑拷打之下,他们交代了一些供词,供词有两类:
1.xi 说 yi 是犯人。
2.xi 说 yi 不是犯人。
犯人们的供词不总是真的,每一个犯人的所有供词最多有一句是假的,而不是犯人的嫌疑人的供词总是真的。
现在给出了全部的 m 条供词,你需要找出哪些人是犯人。如果有多解,输出任何一组解即可
数据范围
n,m<=1e5
题解:
贴好久以前的代码…
当时比较naive,前缀、后缀、供词和人的点都建了,实际上只需要建前缀和人即可。
利用scc判断的方式还未懂,为什么tarjan求到的scc顺序是top序或其逆序的?
这里有一个初步的想法是考虑tarjan的过程,它是弹栈然后求得一些scc,那么先求得的scc的拓扑序靠后,
于是可以直接选择scc编号小(topu靠后)的那些点。
待UPD。
代码:
#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>#include<vector>#include<map>#include<queue>using namespace std;const int N=1000000;const int M=100005;int idc,num,inc,top,cnt;vector<int> pre[M],fake[M],suf[M]; //2*n真 2*n^1假 vector< pair<int,int> > s[M],e;queue<int> q;int head[N],to[10*N],nxt[10*N];bool vis[N];int dfn[N],low[N],stack[N],p[N],in[N],col[N],conf[N ];int n,m;void build(int u,int v){ e.push_back(make_pair(u,v)); num++; to[num]=v; nxt[num]=head[u]; head[u]=num;}void add(int u,int v){ build(u,v); build(v^1,u^1);}void dfs(int u){ inc++; dfn[u]=low[u]=inc; top++; stack[top]=u; vis[u]=1; for(int i=head[u];i;i=nxt[i]) { int v=to[i]; if(!dfn[v]) { dfs(v); if(low[v]<low[u]) low[u]=low[v]; } if(vis[v]&&dfn[v]<low[u]) low[u]=dfn[v]; } if(low[u]==dfn[u]) { cnt++; while(1) { p[stack[top]]=cnt; vis[stack[top]]=0; top--; if(stack[top+1]==u) break; } }}void init(){ memset(head,0,sizeof(head)); memset(in,0,sizeof(in)); num=0; int sz=e.size(); for(int i=0;i<sz;i++) { int u=e[i].first; int v=e[i].second; if(p[u]==p[v]) continue; build(p[u],p[v]); in[p[v]]++; }}void tp(){ memset(col,0,sizeof(col)); for(int i=1;i<=cnt;i++) { if(in[i]==0) { q.push(i); col[i]=1; col[conf[i]]=2; } } while(!q.empty()) { int u=q.front(); q.pop(); for(int i=head[u];i;i=nxt[i]) { int v=to[i]; if(!col[v]) {col[v]=1; col[conf[v]]=2;} in[v]--; if(in[v]==0) q.push(v); } }}int main(){ memset(vis,0,sizeof(vis)); memset(dfn,0,sizeof(dfn)); scanf("%d%d",&n,&m); idc=2*n+1; num=0; while(m--) { int x,y,t; scanf("%d%d%d",&x,&y,&t); s[x].push_back(make_pair(y,t^1)); //奇数真,偶数假 ,记录的是奇数编号 idc+=2; pre[x].push_back(idc); idc+=2; fake[x].push_back(idc); idc+=2; suf[x].push_back(idc); } for(int u=1;u<=n;u++) { int sz=s[u].size(); if(sz) {add(u<<1,pre[u][sz-1]);add(u<<1,suf[u][0]); } for(int i=0;i<sz;i++) { int v=s[u][i].first; if(s[u][i].second) add(fake[u][i],(v<<1)^1); //若此条供词为真,向对应的人建边 else add(fake[u][i],v<<1); if(i!=0) { add(fake[u][i]^1,pre[u][i-1]); add(pre[u][i],pre[u][i-1]);} if(i!=sz-1) {add(fake[u][i]^1,suf[u][i+1]); add(suf[u][i],suf[u][i+1]); } add(pre[u][i],fake[u][i]); add(suf[u][i],fake[u][i]); } } idc++;inc=0;top=0; cnt=0; for(int i=2;i<=idc;i++) { if(!dfn[i]) dfs(i); } for(int i=2;i<=idc;i+=2) { if(p[i]==p[i^1]) { printf("Impossible"); return 0; } if(!conf[p[i]]) { conf[p[i]]=p[i^1]; conf[p[i^1]]=p[i]; } } init(); tp(); cnt=0; for(int i=3;i<=2*n+1;i+=2) { if(col[p[i]]==2) cnt++; } printf("%d\n",cnt); for(int i=3;i<=2*n+1;i+=2) { if(col[p[i]]==2) printf("%d ",i/2); } return 0;}
阅读全文
0 0
- UOJ210【UER #6】寻找罪犯 (2-SAT前后缀优化建边)
- UOJ #210. 【UER #6】寻找罪犯 2-sat 前缀优化建边 详解
- 【UOJ #210】【UER #6】寻找罪犯 (2-SAT)
- 【UOJ #210】【UER #6】寻找罪犯 (2-sat 详解)
- #210. 【UER #6】寻找罪犯
- UOJ 210 [UER #6]寻找罪犯
- [UOJ]210 寻找罪犯 2-Sat 前缀和优化
- [杂题 计数] UOJ#209【UER #6】寻找罪犯
- 【前后缀优化建图+2-SAT】BZOJ3495(PA2010)[Riddle]题解
- [ 前后缀优化建图 2-SAT ] Codeforces587D Duff in Mafia
- [二分答案 2-SAT验证 前后缀优化建图] Codeforces 587D #326 (Div. 1) D. Duff in Mafia
- bzoj3495 PA2010 Riddle(2-SAT 前缀优化建边)
- [二分答案 2-SAT验证 前缀后缀优化建图 线段树优化建图] Codeforces gym 100159 Facebook Hacker Cup 2012 I. Unfriending
- 后缀数组(前篇)
- 2sat建边总结
- 【2-SAT+前缀优化建图】BZOJ3495 PA2010 Riddle
- [UER #6A]票数统计
- A. 【UER #6】票数统计
- JS——闭包
- Retrofit网络请求库应用01
- java--线程-线程安全的原因
- LVS-ipvsadm-虚拟ip轮询调度
- Hibernate二级缓存
- UOJ210【UER #6】寻找罪犯 (2-SAT前后缀优化建边)
- OC 位运算
- 使用Navicat for Oracle新建表空间、用户及权限赋予
- webpack 入门
- 设计模式---工厂模式
- sublime非常好用的文件对比插件--sublimerge
- 感知器
- 【Linux】IPC主题三 -------- 信号二(signal)
- HTML基础(10. 其它)