1. 程式人生 > 其它 >D. Toss a Coin to Your Graph_二分答案+拓撲

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<vct<int> > &eg){
	//用所有數值小於等於x的點建圖 
	//找一條長度大於k的鏈 
	vct<ll> ds(n + 1 , 0) ;
	vct<int> din(n + 1 , 0) ;
	vct<bool> 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<int> 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<vct<int> > 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