CSL 的训练计划(按拓扑序更新最长路)

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;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值