tarjan——洛谷P1262 间谍网络

来源:互联网 发布:mac iphone照片导出 编辑:程序博客网 时间:2024/05/22 03:05

https://www.luogu.org/problem/show?pid=1262
好像很早以前就看懂啊这道题目了;
一直没做;

对于打出NO的,其实很简单:以每个指定的点为起始点做遍历,每到一个新的点就把计数器+1,最后比一下计数器数值与n的大小即可,如果打出NO则再进行一次爆扫即可。
那么如何处理计数器大小等于n,即打出YES的呢?
退一步思考,如果这个有向图是个DAG,怎么办?
这里只需要把所有入度为0的点的权值相加即可。(这一切都建立在能控制所有间谍的基础上)
因为如果不选用入度为0的点,那这些点就是无人控制的。
选了这些点之后,图也一定能被他们及他们的子孙控制,就是说其他备选的点不用选了。
那么好吧,怎么把一个带环有向图变成DAG?

#include<bits/stdc++.h>using namespace std;const int N=30005;struct cs{int to,nxt;}a[200000];int head[N],ll;int c[N][2],tt[N],q[N],lin[N],v[N],A[N],low[N],p[N],pp[N];bool in[N];int n,m,mm,x,y,t,nn,ans;void init(int x,int y){    a[++ll].to=y;    a[ll].nxt=head[x];    head[x]=ll;}void dfs1(int x){    in[x]=1;    for(int k=head[x];k;k=a[k].nxt)        if(!in[a[k].to])dfs1(a[k].to);}void dfs(int x){    tt[x]=++t;low[x]=t;in[x]=1;q[++q[0]]=x;    for(int k=head[x];k;k=a[k].nxt){        if(!tt[a[k].to])dfs(a[k].to);        if(in[a[k].to])low[x]=min(low[x],low[a[k].to]);    }    if(low[x]==tt[x]){        v[++nn]=1e9;        while(1){            lin[q[q[0]]]=nn;            in[q[q[0]]]=0;            v[nn]=min(v[nn],p[q[q[0]]]);            if(q[q[0]--]==x)break;        }    }}int main(){    scanf("%d%d",&n,&m);    memset(p,63,sizeof p);    for(int i=1;i<=m;i++)scanf("%d%d",&x,&y),p[x]=y,pp[i]=x;    scanf("%d",&mm);    for(int i=1;i<=mm;i++){        scanf("%d%d",&x,&y);        init(x,y);        c[i][0]=x;c[i][1]=y;    }    for(int i=1;i<=m;i++)dfs1(pp[i]);    for(int i=1;i<=n;i++)        if(!in[i]){printf("NO\n%d",i);exit(0);} else in[i]=0;    for(int i=1;i<=n;i++)if(!tt[i])dfs(i);    for(int i=1;i<=mm;i++){        x=c[i][0];y=c[i][1];        if(lin[x]==lin[y])continue;        A[lin[y]]++;    }    for(int i=1;i<=nn;i++)if(!A[i])ans+=v[i];    printf("YES\n%d",ans);}