Yali 19 - 8 - 6 test T2 猪国(pig) 题解

T2 猪国

题⽬描述

猪国是⼀个由 \(n\) 个城市组成的国家。
国王意识到了“要致富,先修路”这句话的重要性,它决定⼤规模修路。不巧的是,猪国的
猪们不太会⼯程,于是只能请隔壁鸡国的鸡建狂魔来帮忙修路。鸡建狂魔看不起猪,于是随
便建设了 \(m\) 条单向的路。尽管如此,每条路还是产⽣了或多或少的价值。
路修好了,经济却上不来。国王经过调研,发现了道路的巨⼤缺陷。具体来说,猪⺠们⽅向
感不好,⼀旦存在若⼲条路能组成⼀个环,那么可怜的猪⺠就有可能在环⾥⾯绕来绕去,这
样甚⾄会产⽣反效果。
国王认为这不good。它决定改进这些路使得路再也不能组成环,这样就很good。
国王找到了鸡建狂魔要求售后服务,但鸡建狂魔只答应把若⼲条路反向,同时还要求收取费
⽤。国王不⽢⼼被任意宰割,经过谈判,总费⽤为被反向的路的价值的最⼤值。
鸡建狂魔保证有办法使路变得good。国王想知道把路变得good的最⼩费⽤。当然,如果这
些路本来就很good,那么说明国王的调研有问题,⾃然费⽤就是 0。

输⼊格式

第⼀⾏两个整数 \(n, m\)分别表⽰城市的数⽬和道路的数⽬。
接下来 ⾏,每⾏三个整数\(x , y , z\)表⽰这是⼀条从$ x \(城市单向到\) y \(城市的路,它的价值为\) z $。

输出格式

⼀⾏⼀个整数表⽰答案。

样例

Input 1
5 6
2 1 1
5 2 6
2 3 2
3 4 3
4 5 5
1 5 4
Output 1
2

Input 2
5
7
2 1 5
3 2 3
1 3 3
2 4 1
4 3 5
5 4 1
1 5 3
Output 2
3

数据范围
对于 30% 的数据,n,m <= 20 。
对于 60% 的数据,n,m <= 100 。
对于 100% 的数据,2 <= n, m < 1e5,1 <= x, y <= n, 1 <= z <= 1e9。
数据有梯度。

首先,我们看到出题人很(mo)(ming)(qi)(miao)的题解。

⼆分答案。把所有边权⼤于当前⼆分值的边拿出来建⼀个图。
有环就不⾏,否则就可以(⼩边都从拓扑序⼩点的连向拓扑序⼤的,⼀定没有环)。

但事实上,博主的思路是这样的。(事实上是一样的)

二分答案出一个值(data)(注意,这里的data是“被反向的路的价值的最⼤值”),边权大于这个data的边都不能动(边权小于data的您随意捣鼓)。于是乎就用这些不能动的边造一张图。好嘛,图造好了,拓扑一下,如果叼出来一个环,那显然不符合pig king的要求,那么就得吧data往上升,卡掉环内的一些边。(因为你想让这个图变成一个DAG,你至少得把这个图给摧毁)。哟,升多了,还得降。这样慢慢二分刨刨出来的data,就一定是最合适的data.这样做的话,时间复杂度是O(n log n),没问题

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
const int maxm = 1e5 + 5;
// n , m 的范围。
struct edge{int to, z;};
vector <edge> e[maxn];
// vector 存图
struct group{int u,v,w;}g[maxm];
// 用struct保存每一条边,以便二分时不断建图。
int n, m, x, y, z, cnt, ans;
//ans 是 最终的 data
void init(){
	for(int i = 1;i <= n;i ++){
		e[i].clear();
	}
}
//每次建图之前需要清零
bool work(int limit){
    //拓扑排序
	queue <int> q;
	int rd[maxn] = {0}, dot = 0;
	//dot是拓扑确定顺序的点数的数量
	init();
	for(int i = 1;i <= m;i ++){
		if(g[i].w > limit){
			e[g[i].u].push_back((edge){g[i].v,g[i].w});	
			rd[g[i].v] ++;
		}
	}
	//建图
	for(int i = 1;i <= n;i ++){
		if(rd[i] == 0){
			dot ++;
			q.push(i);
		}
	}
	while(!q.empty()){
		int x = q.front();
		q.pop();
		for(int i = 0;i < e[x].size(); i ++){
			int y = e[x][i].to;
			rd[y] --;
			if(rd[y] == 0){
				q.push(y);
				dot ++;
			}
		}
	}
	//标准的topsort过程
	return dot == n;
	//如果图中有环,那么有些点显然是无法确定顺序的,自然dot != n
}
int main(){
	scanf("%d%d", &n, &m);
	for(int i = 1;i <= m;i ++){
		scanf("%d%d%d", &x, &y, &z); 	
		g[++ cnt] = (group){x,y,z};
	    //记录边数
	}
	int l = 0, r = 1e9;
	//二分data
	while(l <= r){
		int mid = l + r >> 1;//当前data
		if(!work(mid)){//如果当前data都爆出了环,那更小的data您就别想了
			l = mid + 1;
		} else {
		    //如果可以的话,那就再更苛刻的范围内求data
			r = mid - 1;
			ans = mid;
		}
	}
	cout << ans;
	return 0;
}
posted @ 2019-08-07 23:41  永远_少年  阅读(200)  评论(0编辑  收藏  举报