CodeForces 1679D (二分答案, 拓扑排序)

题意 : 给出一个有向图,无重边自环。 选择一段包含k个点的路径,问路径上的最大值最小是多少。
思路:

  • 二分答案,转化为判定性问题。
  • On的判断,可以想到从入度为0的点搜,更新一个dist数组,遇到大于二分的答案x的点v,dist[v] = 0,出现dist大于k的点就判对。处理环:入度仍然不为0的点在环中,想法是dfs一遍,但是多个环有公共点的话,就没法判了。
  • zyz:每次check的时候新建只有不大于x的点权的点的图
    这样就不用再搜环了
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false) ,cin.tie(0), cout.tie(0);
//#pragma GCC optimize(3,"Ofast","inline")
#define ll long long
#define int long long
const int N = 2e5 + 5;
const int M = 1e5 + 5;
const int INF = 0x3f3f3f3f;
const ll LNF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double PI = acos(-1.0);
const double eps = 1e-12;
int a[N], n, m, q[N], k;
int h[N], e[N], ne[N], idx;
int in[N], dist[N];
int U[N], V[N];
void add( int a, int b ) {
 e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
bool check(int x) {
 for ( int i = 1; i <= m; ++ i) {
   if(a[U[i]] > x || a[V[i]] > x ) continue;
   add(U[i], V[i]); ++ in[V[i]];
 }
 int hh = 0, tt = - 1;
 for ( int i = 1; i <= n; ++ i ) {
   if(!in[i] && a[i] <= x ) {
     q[++tt] = i, dist[i] = 1;
     if(dist[i] >= k) return true;
   }
 }
 while( hh <= tt) {
   int u = q[hh++];
   for ( int i = h[u]; ~i; i = ne[i] ) {
     int v = e[i];
     dist[v] = max(dist[u] + 1, dist[v]);
     if(dist[v] >= k ) return true; 
     if( -- in[v] == 0 ) q[++ tt] = v;
   }
 }
 for ( int i = 1; i <= n; ++ i ) {
   if( !in[i] ) continue;
   return true;
 }
 return false;
}
void init() {
 for ( int i = 0; i <= n; ++ i ) in[i] = dist[i] = 0, h[i] = -1;
 idx = 0;
}
signed main() {
 IOS
 memset ( h, -1, sizeof h ) ;
 cin >> n >> m >> k;
 int mx = 0;
 for ( int i = 1; i <= n; ++ i ) {
   cin >> a[i]; mx = max( mx, a[i] );
 }
 for ( int i = 1; i <= m; ++ i ) {
   cin >> U[i] >> V[i];
 }
 int l = 1, r = mx + 1;
 while( l < r ) {
   init();
   int mid = l + r >> 1;
   if( check ( mid ) ) r = mid;
   else l = mid + 1;
 }
 if(l > mx) {
   cout << -1 << '\n'; return 0;
 }
 cout << l << '\n';
 return 0;
}
posted @ 2022-05-15 10:28  qingyanng  阅读(40)  评论(0编辑  收藏  举报