题目大意:A能揭发B,B能揭发C..某些人可以被收买,如果收买A,那么A,B,C..的情报都可以得到。
求能否得到所有情报,如果可以最少花费多少钱去收买。
题解:tajian缩点
dfs/bfs从能收买的人遍历图,如果全部都能遍历,那么可以得
到所有的情报。然后tarjan缩点,并记录缩的每一个点的收买的
最小值,不能收买的人设收买他的花费是正无穷。统计入度为0
的点的收买花费和。
代码:

#include<iostream> #include<cstdio> #include<cstring> #define maxn 3020 #define inf 0x3f3f3f3f using namespace std; int n,p,r,sumedge,top,tim,sumclr,cnt,ans,tot; int head[maxn],Stack[maxn],instack[maxn],low[maxn]; int vis[maxn],dfn[maxn],cw[maxn],rd[maxn],w[maxn],bel[maxn]; struct Edge{ int x,y,nxt; Edge(int x=0,int y=0,int nxt=0): x(x),y(y),nxt(nxt){} }edge[maxn*3]; void add(int x,int y){ edge[++sumedge]=Edge(x,y,head[x]); head[x]=sumedge; } void dfs(int x){ if(vis[x])return; vis[x]=true;++tot; for(int i=head[x];i;i=edge[i].nxt){ int v=edge[i].y; dfs(v); } } void Tarjian(int x){ low[x]=dfn[x]=++tim; Stack[++top]=x;instack[x]=true; for(int i=head[x];i;i=edge[i].nxt){ int v=edge[i].y; if(instack[v])low[x]=min(low[x],dfn[v]); else if(!dfn[v]){ Tarjian(v);low[x]=min(low[x],low[v]); } } if(low[x]==dfn[x]){ sumclr++;cw[sumclr]=inf; while(Stack[top+1]!=x){ bel[Stack[top]]=sumclr; cw[sumclr]=min(cw[sumclr],w[Stack[top]]); instack[Stack[top--]]=false; } } } int main(){ scanf("%d%d",&n,&p); memset(w,0x3f,sizeof(w)); for(int i=1;i<=p;i++){ int x,y; scanf("%d%d",&x,&y);w[x]=y; } scanf("%d",&r); for(int i=1;i<=r;i++){ int a,b; scanf("%d%d",&a,&b); add(a,b); } for(int i=1;i<=n;i++)if(w[i]!=inf)dfs(i); if(tot!=n){ for(int i=1;i<=n;i++) if(vis[i]==0){ printf("NO "); printf("%d ",i); return 0; } } for(int i=1;i<=n;i++)if(!dfn[i])Tarjian(i); for(int x=1;x<=n;x++){ for(int i=head[x];i;i=edge[i].nxt){ int v=edge[i].y; if(bel[x]!=bel[v])rd[bel[v]]++; } } for(int i=1;i<=sumclr;i++) if(rd[i]==0)ans+=cw[i]; printf("YES %d ",ans); return 0; }