D. Toss a Coin to Your Graph...(二分 + 拓扑排序)

Problem - D - Codeforces

 一天,玛莎在公园散步,发现树下有一张图……惊讶吗?你认为这个问题会有一些合乎逻辑和合理的故事吗?没门!所以,问题是……玛莎有一个有向图,第i个顶点包含一个正整数a。最初玛莎可以在某个顶点放一枚硬币。在第一个操作中,她可以将放置在某个顶点u上的硬币移动到任何另一个顶点v上,这样图形中就有一条有方向的边u→v。每次当硬币被放置在某个顶点i时,玛莎在她的笔记本上写下一个整数a(特别是当玛莎最初将硬币放在某个顶点时,她在笔记本上写下一个整数a)。玛莎想要做k -1个运算,这样她笔记本上的最大运算数就越小越好。输入第一行包含三个整数n, m和k (1 <n < 2-105,0 < m< 2- 105,1 < k < 1018) -图中的顶点和边的数量,以及Masha应该做的操作的数量。第二行包含n个整数a (1 < a <10°)——写在图顶点上的数字。下面的m行每一行都包含两个整数u和v (1 < u v < n)——这意味着在图中有一条边u→v。它保证了图不包含循环和多边。输出打印一个整数——玛莎在最佳硬币移动过程中在笔记本上写下的最大数字的最小值。如果玛莎不能执行k -1个操作,则输出-1。

Examples

input

Copy

6 7 4
1 10 2 3 4 5
1 2
1 3
3 4
4 5
5 6
6 2
2 5

output

Copy

4

input

Copy

6 7 100
1 10 2 3 4 5
1 2
1 3
3 4
4 5
5 6
6 2
2 5

output

Copy

10

input

Copy

2 1 5
1 1
1 2

output

Copy

-1

input

Copy

1 0 1
1000000000

output

Copy

1000000000

题解:
要我们求移动k - 1步并且移动步数中最大值最小 ,那我们直接二分枚举最值x即可

把权值小于x的点的边存入途中,然后拓扑排序,找最长边

有两个判断条件,如果最长边>=k说明x值太大,

如果一个点没走过,但是权值 <= x,并且入度不为0,说明存在环

都要r = mid - 1

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<vector>
#include<map>
#include<queue>
#include<set>
using namespace std;
#define int long long
const int N = 4e6 + 10;
typedef pair<int, int> PII;
int n,m,k;
vector<int> p[N];
struct node
{
	int l,r;
}d[N];
int a[N];
int vis[N];
int in[N];
int check(int x)
{
	for(int i = 1;i <= n;i++)
	{
		p[i].clear();
		vis[i] = 0;
		in[i] = 0;
	}
	for(int i = 1;i <= m;i++)
	{
		auto [u,v] = d[i];
		if(a[u] <= x&& a[v] <= x )
		{
			p[u].push_back(v);
			in[v]++;
		}
	}
	queue<PII> q;
	for(int i = 1;i <= n;i++)
	{
		if(in[i] == 0&&a[i] <= x)
		{
			q.push({i,1});
		}
	}
	int mx = 0;
	while(q.size())
	{
		auto t = q.front();
		vis[t.first] = 1;
		mx = max(mx,t.second);
		q.pop();
		for(auto it:p[t.first])
		{
			in[it]--;
			if(in[it] == 0&&a[it] <= x)
			{
				q.push({it,t.second+1});
			}
		}
	}
	int f = 0;
	for(int i = 1;i <= n;i++)
	{
		if(!vis[i] && a[i] <= x&&in[i]!=0)
		{
			f = 1; 

		}
	}
	if(mx >= k)
	f = 1;
	return f;
}
void solve() 
{
	cin >> n >> m >> k;
	for(int i = 1;i <= n;i++)
	{
		cin >> a[i];
	}
	for(int i = 1;i <= m;i++)
	{
		int x,y;
		cin >> x >> y;
		d[i] = {x,y};
	}
	int l = 1,r = 1e9;
	while(l <= r)
	{
		int mid = (l + r)/2;
		if(check(mid))
		r = mid - 1;
		else
		l = mid + 1;
	}
	if(l == 1e9 + 1)
	l = -1;
	cout << l ;
}


signed main() 
{
//	ios::sync_with_stdio(0);
//	cin.tie(0);cout.tie(0);
	int t = 1;
//	cin >> t;
//scanf("%lld",&t);
	while (t--) 
	{
		solve();
	}
}
//3 F
//5 B
//6 F
//9 F
//10 B
//12 F
//15 FB
//18 FB

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值