E. Speedrun

Problem - E - Codeforces

思路:分析题意知给定的图一定是一个有向无环图,所以能够使用拓扑序,在进行拓扑排序的途中统计一个维护一个完成当前任务的最小时间,那么入度为零的点就是一开始的时间,其他的点就是统计所有前驱的最大值max,表示至少在这个最大值之后它才能够完成这个任务,然后我们在看一下max%k与w[u]的关系,如果是大于关系,则代表要完成w[u]必须要到下一天了,因为这一天已经是max%k了,已经比w[u]大了;如果是小于关系,那么能够在这一天完成这个任务。那么我们现在就得到了每个点能够完成的最小的时间,那么统计最大值以及最小值相减就是答案吗?不是的,因为我们通过样例能够发现,我们可以讲某个入度为零的点延后一天完成,这样就使得最小值变大了,但是可能最大值会增加一点点,这样就能够使得差值变小了,那么我们能否枚举所有的入度为零的点,从小到达枚举,然后讲它们延后一天,统计出延后之后的最大值,然后减去最小值呢?这样做是可以的,我们发现每个点最多只会被延后一天(因为假如说u为当前的点,j为其前驱节点,那么如果其中某个j延后了一天,对于u来说,有两种情况,如果延后一天之后也不比它大,说明我u不用延后保持原来的即可,如果延后之后比它大了,说明我必须要延后一天了。但是出现了第二中状态以后,就不会再出现第二种状态了,因为原先u的完成时间是比所有的j都大的,那么如果u+k,那么对于所有的j来说,j+k一定也不会比u+k大了,所有一定是满足条件了,所以每个点最多只会被延后一天,所以每个节点最多只会被遍历一次),那么这样我们就能够保证修改的时间复杂度了,所以就可以直接暴力枚举了

// Problem: E. Speedrun
// Contest: Codeforces - Pinely Round 2 (Div. 1 + Div. 2)
// URL: https://codeforces.com/contest/1863/problem/E
// Memory Limit: 256 MB
// Time Limit: 2000 ms

#include<bits/stdc++.h>
#include<sstream>
#include<cassert>
#define fi first
#define se second
#define i128 __int128
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int,int> PII;
const double eps=1e-7;
const int N=5e5+7 ,M=5e5+7, INF=0x3f3f3f3f,mod=1e9+7,mod1=998244353;
const long long int llINF=0x3f3f3f3f3f3f3f3f;
inline ll read() {ll x=0,f=1;char c=getchar();while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') {x=(ll)x*10+c-'0';c=getchar();} return x*f;}
inline void write(ll x) {if(x < 0) {putchar('-'); x = -x;}if(x >= 10) write(x / 10);putchar(x % 10 + '0');}
inline void write(ll x,char ch) {write(x);putchar(ch);}
void stin() {freopen("in_put.txt","r",stdin);freopen("my_out_put.txt","w",stdout);}
bool cmp0(int a,int b) {return a>b;}
template<typename T> T gcd(T a,T b) {return b==0?a:gcd(b,a%b);}
template<typename T> T lcm(T a,T b) {return a*b/gcd(a,b);}
void hack() {printf("\n----------------------------------\n");}

int T,hackT;
int n,m,k;
int w[N];
int h[N],e[M],ne[M],idx;
ll dist[N];
int din[N];
vector<int> son[N];
ll maxn=-1;

void add(int a,int b) {
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void topsort() {
	queue<int> q;
	for(int i=1;i<=n;i++) {
		if(!din[i]) q.push(i);
	}
	
	while(q.size()) {
		auto it=q.front();
		q.pop();
		
		if(son[it].size()==0) dist[it]=w[it];
		else {
			ll sum=0;
			
			for(int i=0;i<son[it].size();i++) {
				int j=son[it][i];
				
				sum=max(sum,dist[j]);
			}
			
			if((sum%k)<=w[it]) sum+=w[it]-(sum%k);
			else sum+=k-(sum%k)+w[it];
			
			dist[it]=sum;
		}
		
		
		for(int i=h[it];i!=-1;i=ne[i]) {
			int j=e[i];
			
			din[j]--;
			if(din[j]==0) q.push(j);
		}
	}
}

void dfs(int u,ll &maxn) {
	dist[u]=dist[u]+k;
	maxn=max(maxn,dist[u]);
	for(int i=h[u];i!=-1;i=ne[i]) {
		int j=e[i];
		
		if(dist[j]>=dist[u]) continue;
		else dfs(j,maxn);
	}
}

void solve() {
	n=read(),m=read(),k=read();
	
	memset(h,-1,sizeof(int)*(n+4));
	memset(din,0,sizeof(int)*(n+4));
	idx=0;
	for(int i=1;i<=n;i++) w[i]=read(),son[i].clear(),dist[i]=0;
	
	maxn=-1;
	while(m--) {
		int a=read(),b=read();
		add(a,b);
		son[b].push_back(a);
		din[b]++;
	}
	
	vector<int> vis;
	for(int i=1;i<=n;i++) if(!din[i]) vis.push_back(i);
	
	topsort();
	
	for(int i=1;i<=n;i++) maxn=max(maxn,dist[i]);
	
	sort(vis.begin(),vis.end(),[&](int &a,int &b){
		return w[a]<w[b];
	});
	
	ll res=llINF;
	res=min(res,maxn-dist[vis[0]]);
	
	for(int i=0;i<vis.size()-1;i++) {
		dfs(vis[i],maxn);
		res=min(res,maxn-dist[vis[i+1]]);
	}
	
	printf("%lld\n",res);
}   

int main() {
    // init();
    // stin();
	// ios::sync_with_stdio(false); 

    scanf("%d",&T);
    // T=1; 
    while(T--) hackT++,solve();
    
    return 0;       
}          

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值