tarjan——洛谷P1262 间谍网络

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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值