Codeforces Round #791 (Div. 2) D 二分最小化最大值 拓扑排序 非联通图dfs判环

题目

有一天,玛莎在公园里散步,在一棵树下发现了一张图表……很惊讶?你认为这个问题会有一些合乎逻辑和合理的故事吗?没门!那么,问题…

Masha 有一个有向图,其中第 i 个顶点包含一些正整数 ai。最初,玛莎可以将硬币放在某个顶点。在一次操作中,她可以将放置在某个顶点 u 中的硬币移动到任何其他顶点 v,从而在图中存在有向边 u→v。每次将硬币放在某个顶点 i 时,玛莎都会在她的笔记本上写下一个整数 ai(特别是,当玛莎最初将硬币放在某个顶点时,她会在笔记本上写下这个顶点处的一个整数)。 Masha 想要精确地进行 k-1 次操作,使得她笔记本上写的最大数字尽可能小。

输入
第一行包含三个整数 n、m 和 k (1≤n≤2⋅105, 0≤m≤2⋅105, 1≤k≤1018)——图中顶点和边的数量,以及操作次数玛莎应该做的。

第二行包含 n 个整数 ai (1≤ai≤109) — 写在图顶点中的数字。

以下 m 行中的每一行都包含两个整数 u 和 v (1≤u≠v≤n)——这意味着图中存在一条边 u→v。

保证图不包含循环和多边。

输出
打印一个整数——玛莎在最佳硬币运动期间在她的笔记本上写的最大数字的最小值。

如果 Masha 无法执行 k-1 操作,则打印 -1。

题解思路

最小化最大值,先往二分上思考,二分出能写的最大值,再使用小于等于最大值的点来建图,再利用拓扑排序来处理最长链以及环的问题。
如果有环那么这个情况必然可以,如果没有环就检查最长链是否大于K。
ygg的代码是用拓扑排序解决的。
参考ygg
pzr佬就用的别的办法,分出判环以及dp最长链的过程。

这里对不联通单边的图判环使用了一个stack来解决判环问题,以前从来没碰到过。

dp过程比较普通就不拓展了。
参考pzr

AC代码
/*从你的全世界路过.*/
#include <bits/stdc++.h>
//#include <unordered_map>
//priority_queue
#define PII pair<int,int>
#define int long long

using namespace std;

const  int  INF =  0x3f3f3f3f;
const  int  N =  201000 ;
vector <int> head[N] ; 
int a[N] ; 
int vis[N] ; 
int instack[N] ; 
int dp[N] ; 
long long mid ;
int falg = 0 ;  
void dfs(int x )
{
    for (int i = 0 ; i < head[x].size() ; i++ )
    {
        int sk = head[x][i] ; 
        if ( a[sk] <= mid )
        {
            if (instack[sk])
            {
                falg = 1 ; 
                return ; 
            }
            if ( vis[sk] )
                continue;
            instack[sk] = 1 ; 
            vis[sk] = 1 ; 
            dfs(sk) ; 
            instack[sk] = 0 ; 
        }
    }
}
void dfs2(int x )
{
    dp[x] = 1 ; 
    for (int i = 0 ; i < head[x].size() ; i++ )
    {
        int sk = head[x][i] ; 
        if ( a[sk] <= mid )
        {
            if ( !dp[sk] )
                dfs2(sk) ; 
            dp[x] = max(dp[sk]+1,dp[x]) ; 
        }
    }
}
void solve()
{
    long long  n , m , k ;
    cin >> n >> m >> k ;
    for (int i = 1 ; i <= n ; i++ )
        cin >> a[i] ;
    for (int i = 1 ; i <= m ; i++ )
    {
        int t3 , t4 ;
        cin >> t3 >> t4 ;
        head[t3].push_back(t4) ; 
    }
    long long t1 = 1 , t2 = 1e9 + 10 ; 
    while ( t1 < t2 )
    {
        memset(vis,0,sizeof(vis)) ; 
        memset(dp,0,sizeof(dp)) ; 
        memset(instack,0,sizeof(instack)) ; 
        mid = t1 + t2 >> 1 ;
        falg = 0 ; 
        for (int i = 1 ; i <= n ; i++ )
            if ( a[i] <= mid && !vis[i] )
                instack[i] = 1 ,vis[i] = 1 ,dfs(i) , instack[i] = 0 ; 
        if ( falg )
        {
            t2 = mid ; 
            continue ; 
        }
        for (int i = 1 ; i <= n ; i++ )
            if ( a[i] <= mid && !dp[i])
                dfs2(i) ;
        int st = 0 ; 
        for (int i = 1 ; i <= n ; i++ )
            if ( a[i] <= mid )
                st = max(st,dp[i]) ; 
        if ( st >= k )
            t2 = mid ; 
        else
            t1 = mid + 1 ; 
    }
    if ( t2 > 1e9 )
        cout << "-1\n" ; 
    else
        cout << t2 << "\n" ; 
}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
        solve() ;
    return 0 ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值