洛谷P4819 [中山市选]杀人游戏
~~ 蒟蒻第一次发CSDN~~
这道题浪费了我好长时间
首先,这肯定是一道tarjan的题
然后native的我只写拉个简单的缩点
然后可想而知了。。。
这题需要考虑到有一些人是与世隔绝的人(即他可能了解别人,但别人不了解他)身为大蒟蒻的我没想到,所以A完后一定要发题解总结
所以这道题处理一下那些特殊的人即可
那些特殊的人就是大小为1且入度为0的点(缩点后)并且他指向的点入度不为1(通俗的说,就是可以通过别的人了解他知道的人,不必通过他),那么我们就可以少一次询问,则标记flag=1
但要注意的是,只可以减小一次询问,因为在有n个人的情况下,问完n-1个人后,若无罪犯,则第n个即为
注意写代码一定要仔细
不然会。。。蒟蒻自己的提交记录
偶对,差点忘了,蒟蒻有个错误算法,大家不要犯,就是求下连通块,当连通块数大于一,且入度为零时,标记flag,最后若flag被标记减去一次op,但这是不对的,考虑情况不全面,漏掉了正解考虑的一些情况,大家思考一下应该可以懂得蒟蒻错在了哪里
蒟蒻的错误代码
for(int i=1;i<=m;i++){
int xx=sd[e[i].u];
int yy=sd[e[i].v];
if(xx!=yy){
in[yy]++;
}
}
for(int i=1;i<=ecnt;i++)
if(!in[i]) op++; 共有op个联通快;
if(op>=2)
for(int i=1;i<=ecnt;i++){
if(!in[i]&&size[i]==1){
flag=1;
break;
}
} 这里考虑的情况不全,然后挂了;
if(flag) op--;
改正错误才能进步嘛。。。
最后上正确代码吧(蒟蒻代码很好懂,不需要解释并且自我认为码风也不错)
#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=100010;
int n,m,cnt,top,tim,ecnt,flag,nxt;
int head[maxn],dfn[maxn],low[maxn],stc[maxn],sd[maxn],in[maxn],h[maxn],size[maxn];
bool vis[maxn];
double op;
struct Edge{
int u,v,next;
}e[maxn*3],a[maxn*3];
void add(int u,int v){
e[++cnt].u=u;
e[cnt].v=v;
e[cnt].next=head[u];
head[u]=cnt;
return ;
}
void Tarjan(int x){
dfn[x]=low[x]=++tim;
stc[++top]=x; vis[x]=1;
for(int i=head[x];i;i=e[i].next){
int v=e[i].v;
if(!dfn[v]){
Tarjan(v);
low[x]=min(low[x],low[v]);
}
else if(vis[v])
low[x]=min(low[x],dfn[v]);
}
if(low[x]==dfn[x]){
int y;
ecnt++;
while(y=stc[top--]){
sd[y]=ecnt;
vis[y]=0;
size[ecnt]++;
if(y==x) break;
}
}
}
int main(){
int x,y;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
add(x,y);
}
for(int i=1;i<=n;i++)
if(!dfn[i]) Tarjan(i);
for(int i=1;i<=m;i++){
int xx=sd[e[i].u];
int yy=sd[e[i].v];
if(xx!=yy){
in[yy]++;
a[++nxt].u=xx;
a[nxt].v=yy;
a[nxt].next=h[xx];
h[xx]=nxt;
}
}
for(int i=1;i<=ecnt;i++)
if(!in[i]) op++;
for(int i=1;i<=ecnt;i++)
if(!flag&&!in[i]&&size[i]==1){
int pd=0;
for(int j=h[i];j;j=a[j].next)
if(in[a[j].v]==1) pd=1;
if(!pd){
flag=1;
break;
}
}
if(flag) op--;
printf("%0.6lf",(double)1-op/n);
return 0;
}