洛谷 1262 间谍网络——缩点+拓扑
题目:https://www.luogu.org/problemnew/show/P1262
当然是缩点。一个点的收买价就是旗下点的最小值。然后从入度为0的点挨个dfs。
但其实不对。可能入度为0的那个开头是不能收买的,但后面一个有入度的点可以收买。
所以应该像拓扑排序那样遍历每个点。注意打过vis标记就不再算了。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=3005,M=8005,INF=0x3f3f3f3f; int n,p,m,a[N],hd[N],xnt,to[M],nxt[M],fr[M]; int dfn[N],low[N],tim,col[N],cnt,rd[N],c[N]; int ans,sta[N],top,q[N],he,tl; bool ins[N]; struct Ed{ int nxt,to;Ed(int n=0,int t=0):nxt(n),to(t) {} }ed[M]; int rdn() { int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9') ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar(); return fx?ret:-ret; } void add(int x,int y) { to[++xnt]=y; nxt[xnt]=hd[x]; hd[x]=xnt; fr[xnt]=x; } void ade(int x,int y) { ed[++xnt]=Ed(hd[x],y); hd[x]=xnt; rd[y]++; } void tarjan(int cr) { dfn[cr]=low[cr]=++tim; sta[++top]=cr; ins[cr]=1; for(int i=hd[cr],v;i;i=nxt[i]) { if(!dfn[v=to[i]]) tarjan(v),low[cr]=min(low[cr],low[v]); else if(ins[v]) low[cr]=min(low[cr],dfn[v]); } if(dfn[cr]==low[cr]) { cnt++; c[cnt]=INF; while(sta[top]!=cr) { int k=sta[top--]; col[k]=cnt; ins[k]=0; c[cnt]=min(c[cnt],a[k]); } top--; col[cr]=cnt; ins[cr]=0; c[cnt]=min(c[cnt],a[cr]); } } void dfs(int cr) { ins[cr]=1; for(int i=hd[cr];i;i=ed[i].nxt) dfs(ed[i].to); } int main() { n=rdn(); p=rdn(); memset(a,0x3f,sizeof a); for(int i=1,u;i<=p;i++) { u=rdn();a[u]=rdn(); } m=rdn(); for(int i=1,u,v;i<=m;i++) { u=rdn(); v=rdn(); add(u,v); } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); xnt=0; memset(hd,0,sizeof hd); for(int i=1;i<=m;i++) if(col[to[i]]!=col[fr[i]]) ade(col[fr[i]],col[to[i]]); memset(ins,0,sizeof ins); for(int i=1;i<=cnt;i++) if(!rd[i]) q[++tl]=i; while(he<tl) { int k=q[++he]; if(c[k]<INF&&!ins[k]) ans+=c[k],dfs(k); for(int i=hd[k],v;i;i=ed[i].nxt) { rd[v=ed[i].to]--; if(!rd[v]) q[++tl]=v; } } for(int i=1;i<=n;i++) if(!ins[col[i]]) { printf("NO\n%d\n",i); return 0; } printf("YES\n%d\n",ans); return 0; }