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