ACM题集:https://blog.csdn.net/weixin_39778570/article/details/83187443
题目链接:https://ac.nowcoder.com/acm/contest/551/G
众所周知,CSL 是一个负责的集训队队长。为了让集训队的学弟们训练更加饱和,他根据每个人的能力,提出了 m 个题数要求。假如 CSL 认为 yi 比 xi 强,那么如果 xi 做了 a 题,那 CSL 会要求 yi 需要做至少 a+ri×k,其中 ri 是已知的常数。CSL 现在一共有 s 道题目可以分给大家,因为 CSL 马上就要考OS了,所以他不想再出其他题了,请问正整数 k 最大是多少。
强度是具有传递性的,如果 x 比 y 强且 y 比 z 强,那么 CSL 不会认为 z 比 x 强。
所以可以显然发现这是一幅无环图,====>也可看成这样的"森林", 有多棵"树",但是父节点不一定只来自一个点,
如果有这么一条链 x > y > z > t
那么我们设
x的做题数为0, 即ax=0
y的做题数为 ay = ax + k*r1 = k*r1
z的做题数为 az = ay + k*r2 = k*(r1+r2)
t的做题数为 at = az + k*r3 = k*(r1+r2+r3)
那么dis[x] = 0, dis[y] = r1, dis[z] = (r1+r2), dis[t] = (r1+r2+r3)
显然 建立 x->y的边,边权为r
然后直接bfs,入度为0的时候入队列更新dis,使其最大
ps:当一个点入度为0的时候说明它已经更新完毕了
那么答案为 s/∑(dis[i]) (有解的情况,无解为0,∑(dis[i])=0则输出-1)
#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
const int N=2e5+5,M=6e5+5;
int n,m;
ll s,dis[N];
int head[N],ver[M],Next[M],edge[M],tot,inq[N];
void add(int x, int y, int z){
ver[++tot]=y, edge[tot]=z;
Next[tot]=head[x],head[x]=tot;
}
void solve(){
queue<int> q;
fo(i,1,n)if(inq[i]==0)q.push(i); // 加入入度为0的点,是一颗树的根,起始同学的a值均设置为0
// bfs
while(q.size()){
int x=q.front();q.pop();
for(int i=head[x]; i; i=Next[i]){
int y=ver[i], z=edge[i];
if(dis[y]<dis[x]+z){
dis[y] = dis[x]+z;
}
if(--inq[y]==0)q.push(y); // 入度为0后,成为新的树根入队
}
}
ll sum = 0;
fo(i,1,n)sum+=dis[i];// 图上的各个点的计算出的最大r(dis[i])累加
if(sum==0)puts("-1");
else if(sum>s)puts("0");
else printf("%lld\n", s/sum); // 在所有原本的根的a设为0后,s/sum,取下界即为k最大值
}
int main(){
scanf("%d%d%lld",&n,&m,&s);
int x,y,z;
fo(i,1,m){
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
inq[y]++; // 入度
}
solve();
return 0;
}