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;
}