P10957 环路运输题解

题目链接:https://www.luogu.com.cn/problem/P10957
题目大意:在一个环形区域上,任意两个点都有一个权值,这个权值表示为 w = ai + aj + min ( | i - j |, N - | i - j | ) ,求max( w )

分析:我们看式子中的min ( | i - j | , N - | i - j | ) ,就是求从i到j怎么走最近,是走一个环路,还是直线从i到j

1、遇到环的问题,尝试展开为2倍链子,这样, 若认为i >= j , 则 w = ai + aj + i - j ,顺便解决了从i到j的最短走法问题, 如下图

2、这里有两个变量,如果i和j直接枚举,时间复杂度是平方级的,超时
不妨固定一个,假设i固定了,就需要去枚举j就可以了,所以,当i固定时, w = ai + i + aj - j , 要想w最大,i是固定的,只需要让aj-j最大就可以了
即: w = ai + i + max ( aj - j )
i和j什么关系呢?i-j不得超过n/2,从环路图看,因为一旦二者距离超过n/2了,i-j的距离就需要以图中2号示意那样走,所以问题就转成了:
计算 : i点已确定,在 i - j <= n/ 2 ,就是 j >=i - n / 2 的基础上,找个j让aj -j 最大
核心在这块,在i ~ i - n /2 这个窗口内,找个j,让aj - j 最大 ---区间内的最大值问题,可以联想到单调队列,或者堆
3、单调队列维护代码如( 注意i !=j ) ,时间复杂度O( n )

点击查看代码
#include <bits/stdc++.h>
using namespace std   ;
const int N = 2e6+ 10 ;
long long a [ N ] , q[ N ] , r = 1, f= 1  ;
long long   ans ;
int n  ;
int main (  )
{
    cin >> n  ;
    for ( int i = 1 ; i <= n ; i ++ ) { cin >> a[ i ] ;a[ i + n ] = a[ i ] ; }
    for ( int i = 1 ; i <= n + n  ; i ++ )
    { ans = max ( ans , a [ i ] + i + a [ q[f ] ]  - q [ f ] ) ;
        while ( f < r &&( a [ q [ r - 1 ] ] - q [ r - 1 ] <= a [ i ] - i  )  )
            r -- ;
        q [ r ++ ]= i  ;
        if ( f < r && q[ f ] <= i - n / 2  ) f ++ ;
    }
    cout <<ans ;
    return   0 ;
}

4、堆维护,时间复杂度o( nlogn ) n=2e6
参考代码:

点击查看代码
#include <bits/stdc++.h>
using namespace std   ;
const int N = 2e6+ 10 ;
long long a [ N ]   ;
typedef  pair <int,int> PII ;
priority_queue <PII> q    ;
long long   ans ;
int n  ;
int main (  )
{
    cin >> n  ;
    q.push ( { 0 , 0 } ) ;
    for ( int i = 1 ; i <= n ; i ++ ) { cin >> a[ i ] ;a[ i + n ] = a[ i ] ; }
    for ( int i = 1 ; i <= n + n  ; i ++ )
    {    while ( q.size (  ) && q.top ( ) .second < i - n / 2)
               q.pop (  ) ;
          PII t = q.top (  ) ;
          ans = max ( ans , a[ i ] + i + q.top( ). first ) ;
          q.push ( { a [i ] - i , i } ) ;
    }
    cout <<ans ;
    return   0 ;
}

posted @ 2025-02-20 09:10  buyan00  阅读(54)  评论(0)    收藏  举报