间谍网络(Tarjan缩点)

由于外国间谍的大量渗入,国家安全正处于高度危机之中。如果 A间谍手中掌握着关于 B 间谍的犯罪证据,则称 A 可以揭发 B。有些间谍接受贿赂,只要给他们一定数量的美元,他们就愿意交出手中掌握的全部情报。所以,如果我们能够收买一些间谍的话,我们就可能控制间谍网中的每一分子。因为一旦我们逮捕了一个间谍,他手中掌握的情报都将归我们所有,这样就有可能逮捕新的间谍,掌握新的情报。
我们的反间谍机关提供了一份资料,包括所有已知的受贿的间谍,以及他们愿意收受的具体数额。同时我们还知道哪些间谍手中具体掌握了哪些间谍的资料。假设总共有 n 个间谍,每个间谍分别用 1 到 3000 的整数来标识。
请根据这份资料,判断我们是否可能控制全部的间谍,如果可以,求出我们所需要支付的最少资金。否则,输出不能被控制的一个间谍。


首先我们可以看出来的是要区分联通块,所以由此想到了tarjan,然后我们来看对于一个连通分量,我们只需要在里面有一个点就可以

所以想到Tarjan缩点,每个店的点权就是原先联通分量里的最小的点权

然后我们发现只求出联通分量是不够的

因为可能联通分量之间还有边,而只要有一条入边,就不用再选了

所以需要花费的只有入读为0的边

下面给出代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
inline int rd(){
    int x=0,f=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
inline void write(int x){
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
    return ;
}
int n,m,k;
int head[100006],nxt[100006],to[100006];
int total=0;
void add(int x,int y){
    total++;
    to[total]=y;
    nxt[total]=head[x];
    head[x]=total;
    return ;
}
int v[100006];
int dfn[100006];
int low[100006];
int tot=0;
int color[100006];
int book[100006];
int sta[100006],set=0;
int dis[100006];
int cnt=0;
void tarjan(int x){
    low[x]=dfn[x]=++tot;
    book[x]=1;
    sta[++set]=x;
    for(int e=head[x];e;e=nxt[e]){
        if(!dfn[to[e]]){
            tarjan(to[e]);
            low[x]=min(low[x],low[to[e]]);
        }
        else if(book[to[e]]) low[x]=min(low[x],dfn[to[e]]);
    }
    if(dfn[x]==low[x]){
        dis[++cnt]=min(dis[cnt],v[x]);
        color[x]=cnt;
        book[x]=0;
        while(set&&sta[set]!=x){
            dis[cnt]=min(dis[cnt],v[sta[set]]);
            color[sta[set]]=cnt;
            book[sta[set]]=0;
            set--;
        }
        set--;
    }
    return ;
}
int du[100006];
int main(){
    memset(dis,127,sizeof(dis));
    memset(v,127,sizeof(v));
    n=rd(),k=rd();
    for(int i=1;i<=k;i++){
        int x=rd();
        v[x]=rd();
    }
    m=rd();
    for(int i=1;i<=m;i++){
        int x=rd(),y=rd();
        add(x,y);
    }
    for(int i=1;i<=n;i++) if(!dfn[i]&&v[i]!=2139062143) tarjan(i);
    for(int i=1;i<=n;i++) if(!dfn[i]){
        printf("NO\n");
        write(i);
        return 0;
    }
    printf("YES\n");
    for(int i=1;i<=n;i++) for(int e=head[i];e;e=nxt[e]) if(color[i]!=color[to[e]]) du[color[to[e]]]++;
    int ans=0;
    for(int i=1;i<=cnt;i++) if(!du[i]) ans+=dis[i];
    write(ans);
    return 0;
}

 

posted @ 2018-10-25 20:30  Bruce--Wang  阅读(219)  评论(0编辑  收藏  举报