队爷的讲学计划
问题描述
队爷为了造福社会,准备到各地去讲学。他的计划中有n 个城市,从 u 到 v 可能有一条单向道路,通过这条道路所需费用为 q。当队爷在 u 城市讲学完之后,u 城市会派出一名使者与他同行,只要使者和他在一起,他到达某个城市就只需要花 1 的入城费且只需交一次,在路上的费用就可免去。。但是使者要回到 u 城市,所以使者只会陪他去能找到回 u 城市的路的城市。。队爷从 1 号城市开始讲学,若他在u 号城市讲学完毕,使者会带他尽可能多的去别的城市。
他希望你帮他找出一种方案,使他能讲学到的城市尽可能多,且费用尽可能小。
输入文件
第一行 2 个整数 n,m。
接下来 m 行每行 3 个整数 u,v,q,表示从 u 到 v 有一条长度为 q的单向道路。
输出文件
一行,两个整数,为最大讲学城市数和最小费用。
数据规模与约定
对于 20%的数据,1<=n<=20;
对于另外 10%的数据,城市网络为一条单向链;
对于 60%的数据,1<=m<=200000
对于 100%的数据,1<=n<=100000
1<=m<=500000,1<=q<=1000, 保证无自环无重边。
分析
对于那些可以回到自己的点一定在一个强连通分量中,所以先用tarjan算法缩点重新建边之后得到一个DAG(有向无环图),可以用拓扑排序,也可以用spfa,这题数据不卡spfa。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int N=100010; const int M=500010; inline int read(){ int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n,m,tot,idx,top,cnt,ans; int head[N],dis[N],u[M],v[M],w[M],f[N],g[N]; int dfn[N],low[N],sta[N],B[N],du[N],s[N]; bool instack[N]; queue<int>q; struct node{ int next,to,dist; }e[M]; inline void ins(int from,int to,int dist){ e[++tot].next=head[from]; e[tot].to=to; e[tot].dist=dist; head[from]=tot; } void tarjan(int x){ dfn[x]=low[x]=++idx; sta[++top]=x; instack[x]=true; for(int i=head[x];i;i=e[i].next) if(!dfn[e[i].to]){ tarjan(e[i].to); low[x]=min(low[x],low[e[i].to]); }else if(instack[e[i].to]) low[x]=min(low[x],dfn[e[i].to]); if(dfn[x]==low[x]){ ++cnt; int y; do{ y=sta[top--]; instack[y]=false; B[y]=cnt; ++s[cnt]; }while(x!=y); } } void dfs(int x){ for(int i=head[x];i;i=e[i].next) if(++du[e[i].to]==1) dfs(e[i].to); } int main(){ freopen("teach.in","r",stdin); freopen("teach.out","w",stdout); n=read();m=read(); for(int i=1;i<=m;++i){ u[i]=read();v[i]=read();w[i]=read(); ins(u[i],v[i],w[i]); } for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i); tot=0;memset(head,0,sizeof(head)); for(int i=1;i<=m;++i) if(B[u[i]]!=B[v[i]]) ins(B[u[i]],B[v[i]],w[i]); memset(g,0x3f,sizeof(0x3f)); f[B[1]]=g[B[1]]=s[B[1]]; --g[B[1]]; dfs(B[1]); q.push(B[1]); while(!q.empty()){ int x=q.front();q.pop(); for(int i=head[x];i;i=e[i].next){ int to=e[i].to; --du[to]; if(!du[to]) q.push(to); if(f[x]+s[to]>f[to]){ f[to]=f[x]+s[to]; g[to]=g[x]+e[i].dist+s[to]-1; }else if(f[x]+s[to]==f[to]) g[to]=min(g[to],g[x]+e[i].dist+s[to]-1); } } for(int i=1;i<=cnt;++i) if(f[i]>f[ans]) ans=i; else if(f[i]==f[ans]&&g[i]<g[ans]) ans=i; printf("%d %d ",f[ans],g[ans]); return 0; }