牛客上海高校程序设计竞赛 G CSL 的训练计划(拓扑排序)

链接:https://ac.nowcoder.com/acm/contest/551/G
来源:牛客网

时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 524288K,其他语言1048576K
64bit IO Format: %lld
题目描述
众所周知,CSL 是一个负责的集训队队长。为了让集训队的学弟们训练更加饱和,他根据每个人的能力,提出了 m 个题数要求。假如 CSL 认为 yi 比 xi 强,那么如果 xi 做了 a 题,那 CSL 会要求 yi 需要做至少 a+ri×k,其中 ri 是已知的常数。CSL 现在一共有 s 道题目可以分给大家,因为 CSL 马上就要考OS了,所以他不想再出其他题了,请问正整数 k 最大是多少。
输入描述:
第一行有三个整数 n, m, s,分别表示集训队的学弟数量,CSL 的题数要求和 CSL 的题目数量。

接下来 m 行,每行三个整数
xi,yi,ri,含义题目描述中所述。
2≤n≤2⋅105
1≤m≤6⋅105
1≤s≤1012
1≤xi,yi≤n
0≤ri≤106
输出描述:
在一行输出一个整数表示 k 可取的最大值。特别地,如果题目不够分则输出 0;为无穷大输出 -1。
示例1
输入
4 5 19
1 3 0
3 4 4
1 4 2
1 3 2
2 4 1
输出
2
示例2
输入
5 5 6
5 4 2
3 2 1
3 5 3
2 4 4
5 2 1
输出
0
备注:
强度是具有传递性的,如果 x 比 y 强且 y 比 z 强,那么 CSL 不会认为 z 比 x 强。

输入数据量较大,建议使用高效的输入输出方式。例如:在 C++ 中使用 scanf/printf 代替 cin/cout;在 Java 中使用 BufferedReader/PrintWriter 代替 Scanner/System.out。
(我的超时思路:当时做的时候想到二分枚举答案,然后深搜检验,当时觉得思路巨好,结果一直T,还是太菜了。。。)
(思路:只有所有r都为0,才会出现“-1”这种情况。否则,直接一遍拓扑求出需要的最少的题数,用s除以该值即可。当时没有画图,想着,孙子节点的值是其爷爷节点的值得K的二次方倍。其实,对于每个节点,其值是该节点到根节点的最远距离乘以k。(其实看不出来,直接二分枚举k,拓扑排序检验也可以。)k最小为1,先按1求出每个节点的值,然后求其和sum,那么答案就是s/sum。)

(至于搜索为啥超时,大牛队友给了一种case。)
牛客上海高校程序设计竞赛 G CSL 的训练计划(拓扑排序)
1.直接拓扑排序代码(380ms)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#include <stack>
#include <set>
#include <cmath>
#include <string>
#define ll long long
using namespace std;
const int maxn=2e5+10;
const int maxm=6e5+10;
int n,m;
ll s;
int head[maxn],cnt;
int in[maxn];
bool vis[maxn];
ll ans[maxn];
struct node
{
	int to,next,w;
}E[maxm];
void add(int u,int v,int w)
{
	E[cnt].to=v;
	E[cnt].w=w;
	E[cnt].next=head[u];
	head[u]=cnt++;
}
void Toposort()
{
   ll sum=0;
   queue <int> q;	
   for(int i=1;i<=n;i++)
   {
   	    vis[i]=0;
   		ans[i]=0;
   		if(in[i]==0)
   		{
   			q.push(i);
			vis[i]=1;	
		}
   	    
   }
   while(q.size())
   {
   		int now=q.front();
		q.pop();
		vis[now]=0;
		for(int i=head[now];i!=-1;i=E[i].next)
		{
				int v=E[i].to;
				ll tmp=ans[now]+E[i].w;
				ans[v]=max(ans[v],tmp);
				if(--in[v]==0&&!vis[v])
			    {
			    	q.push(v);
			    	vis[v]=1;
				}
		}	
   }
   for(int i=1;i<=n;i++)  
   {
   		sum+=ans[i];
   }
   printf("%lld\n",s/sum);	
}
int main(void)
{
   scanf("%d%d%lld",&n,&m,&s);
   int u,v,w,f=1;
   cnt=0;
   for(int i=1;i<=n;i++)
   {
   	 head[i]=-1;
   }
   for(int i=0;i<m;i++)
   {
   		scanf("%d%d%d",&u,&v,&w);
   		if(u==v) continue;
   		in[v]++;
   		if(w!=0) f=0;
   		add(u,v,w);
   }
   if(f)
   {
   		printf("-1\n");
   		return 0;
   }
   Toposort();
   return 0;
}

2.二分+拓扑(1589ms)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#include <stack>
#include <set>
#include <cmath>
#include <string>
#define ll long long
using namespace std;
const int maxn=2e5+10;
const int maxm=6e5+10;
int n,m;
ll s;
int head[maxn],cnt;
int in[maxn],temp[maxn];
bool vis[maxn];
ll ans[maxn];
struct node
{
	int to,next,w;
}E[maxm];
void add(int u,int v,int w)
{
	E[cnt].to=v;
	E[cnt].w=w;
	E[cnt].next=head[u];
	head[u]=cnt++;
}
bool check(ll k)
{
   ll sum=0;
   queue <int> q;	
   for(int i=1;i<=n;i++)
   {
   	    vis[i]=0;
   		ans[i]=0;
   		temp[i]=in[i];
   		if(in[i]==0)
   		{
   			q.push(i);
			vis[i]=1;	
		}
   	    
   }
   while(q.size())
   {
   		int now=q.front();
		q.pop();
		vis[now]=0;
		for(int i=head[now];i!=-1;i=E[i].next)
		{
				int v=E[i].to;
				ll tmp=ans[now]+E[i].w*k;
				if(tmp>s)
				{
					return 0;
				}
				ans[v]=max(ans[v],tmp);
				if(--temp[v]==0&&!vis[v])
			    {
			    	q.push(v);
			    	vis[v]=1;
				}
		}	
   }
   
   for(int i=1;i<=n;i++)  
   {
   		sum+=ans[i];
   		if(sum>s) return 0;
   }
   return 1;	
}
int main(void)
{
   scanf("%d%d%lld",&n,&m,&s);
   int u,v,w,f=1;
   cnt=0;
   for(int i=1;i<=n;i++)
   {
   	 head[i]=-1;
   }
   for(int i=0;i<m;i++)
   {
   		scanf("%d%d%d",&u,&v,&w);
   		if(u==v) continue;
   		in[v]++;
   		if(w!=0) f=0;
   		add(u,v,w);
   }
   if(f)
   {
   		printf("-1\n");
   		return 0;
   } 
   ll l=0,r=2*s,res=0;
   while(l<=r)
   {
   	    ll m=(l+r)/2;
    	if(check(m))
   	    {
   	       res=max(m,res);
   	       l=m+1;	
		}
   	    else 
   	    {	
   	    	r=m-1;	
		}
   }
   printf("%lld\n",res);
   return 0;
}