D. Toss a Coin to Your Graph_二分答案+拓扑


D. Toss a Coin to Your Graph_二分+拓扑

题目大意

在图中找一个长度大于等于k的链使得其中的最大值最小。

思路和代码

很好的题目

首先,最大值的最小很容易就可以想到二分

每次在值小于等于mid的图里面做拓扑序bfs,维护ds数组,dsi表示从拓扑序起点到点i的最多点数。

ll n , m , k ; 

int a[N] ;

bool ck(ll x , vct > &eg){
	//用所有数值小于等于x的点建图 
	//找一条长度大于k的链 
	vct ds(n + 1 , 0) ;
	vct din(n + 1 , 0) ;
	vct ing(n + 1 , 0) ;
	int num = 0 ;
	rep(u , 1 , n){
		if(a[u] > x) continue ;
		num ++ ;
		ing[u] = 1 ;
		for(int v : eg[u]){
			if(a[v] > x) continue ;
			din[v] ++ ;
		}
	}
	
	queue q ;
	rep(i , 1 , n)
	if(ing[i] && !din[i]){
		q.push(i) ;
		ds[i] = 1 ;
	}
	while(q.size()){
		int now = q.front() ;
		q.pop() ;
		num -- ;
		for(int nxt : eg[now]){
			if(!ing[nxt]) continue ;
			ds[nxt] = max(ds[nxt] , ds[now] + 1LL) ;
			if(! -- din[nxt]) q.push(nxt) ;
		}
	}

	if(num > 0) return 1 ;
	rep(i , 1 , n)
	if(ing[i] && ds[i] >= k) return 1 ;
	return 0 ;
}

void solve(){
	cin >> n >> m >> k ;
	get1(a , 1 , n) ;
	
	vct > eg(n + 1) ;
	rep(i , 1 , m){
		int u , v ;
		cin >> u >> v ;
		eg[u].pb(v) ;
	}
	
	ll l = 1 , r = mod ;
	while(l < r){
//		cout << l << " " << r << "," ;
		int mid = l + r >> 1 ;
		if(ck(mid , eg)) r = mid ;
		else l = mid + 1 ;
	}
	cout << (l < mod ? l : -1) << "\n" ;
}//code_by_tyrii