<题目链接>
题目大意:
听说lcy帮大家预定了新马泰7日游,Wiskey真是高兴的夜不能寐啊,他想着得快点把这消息告诉大家,虽然他手上有所有人的联系方式,但是一个一个联系过去实在太耗时间和电话费了。他知道其他人也有一些别人的联系方式,这样他可以通知其他人,再让其他人帮忙通知一下别人。你能帮Wiskey计算出至少要通知多少人,至少得花多少电话费就能让所有人都被通知到吗?
解题分析:
强连通缩点,再将所有入度为0的强连通分量中最小的价值相加即可。
#include <bits/stdc++.h> using namespace std; #define pb push_back #define rep(i,s,t) for(int i=s;i<=t;i++) #define clr(a,b) memset(a,b,sizeof(a)) const int N = 1e3+10,M = 2e3+10; int n,m,tot,cnt,scc,top; int val[N],instk[N],stk[N],dfn[N],low[N],ind[N],outd[N],belong[N],head[N]; struct Edge{ int to,next; }edge[M]; vector<int>G[N]; void init(){ rep(i,0,n)G[i].clear(); tot=cnt=scc=top=0; clr(instk,0);clr(stk,0);clr(dfn,0);clr(low,0);clr(ind,0);clr(outd,0);clr(belong,0),clr(head,-1); } void addedge(int u,int v){ edge[++cnt].to=v,edge[cnt].next=head[u]; head[u]=cnt; } void Tarjan(int u){ dfn[u]=low[u]=++tot; stk[++top]=u;instk[u]=1; for(int i=head[u];~i;i=edge[i].next){ int v=edge[i].to; if(!dfn[v]){ Tarjan(v); low[u]=min(low[u],low[v]); }else if(instk[v])low[u]=min(low[u],dfn[v]); } if(dfn[u]==low[u]){ scc++; while(true){ int v=stk[top--]; instk[v]=0; belong[v]=scc; G[scc].pb(v); if(v==u)break; } } } int main(){ while(scanf("%d%d",&n,&m)!=EOF){ init(); rep(i,1,n)scanf("%d",&val[i]); rep(i,1,m){ int u,v;scanf("%d%d",&u,&v); addedge(u,v); } rep(i,1,n) if(!dfn[i])Tarjan(i); rep(u,1,n) for(int i=head[u];~i;i=edge[i].next){ int v=edge[i].to; if(belong[u]!=belong[v]) ind[belong[v]]++,outd[belong[u]]++; } int ans=0,res=0; rep(i,1,scc) if(!ind[i]){ int mn=1e9; rep(j,0,G[i].size()-1){ //遍历该连通块中的所有元素,寻找价值最小的 int v=G[i][j]; mn=min(mn,val[v]); } res++; ans+=mn; } printf("%d %d ",res,ans); } }