ACWing 289 环路运输

Problem

给定\(n\)个数\(a_1,\cdots,a_n\),定义\(d(i,j) = a_i + a_j + dist(i,j)\),其中\(dist(i,j) = \min(|i - j|,n - |i - j|)\)
求一组点对\((i_0,j_0)\),使得\(d(i_0,j_0)\)最大。
\(n \le 1 \times 10^6,a_i \le 1 \times 10^7\)

Solution

Thinking 1

枚举\(i,j\)\(\mathcal{O}(n^2)\)

Thinking 2

发现这个\(dist(i,j)\)其实就是\(i\)\(j\)在环上的距离。那么破换成链,可以将\(dist\)变简洁。

Thinking 3

倍长\(a\),那么问题变成:在新序列中找一组点对\((i_0,j_0)\),使得\(d(i_0,j_0) = a_{i_0} + a_{j_0} + i_0 - j_0(i_0 > j_0,i_0 - j_0 < n / 2)\)最大。
考虑对于每一个位置\(i\),找到最大的\(j\),发现\(a_{i_0} + i_0\)是固定的,所以题目变成:枚举\(i\),找到\(a_i + i + \left(\max_{\\j < i,i - j < n / 2}\{a_j - j\}\right)\)
然后这玩意显然可以单调队列啊,\(\mathcal{O}(n)\)

# include <bits/stdc++.h>
using namespace std;
# define int long long
const int N = 2e6 + 5;
int n;
int a[N];
int q[N];
int ans = 0;
int head = 1,tail = 0;
signed main(void)
{
    scanf("%lld",&n);
    for(int i = 1; i <= n; i++) {scanf("%lld",&a[i]); a[i + n] = a[i];}
    n <<= 1;
    int len = n / 4;
    q[++tail] = 1;
    for(int i = 2; i <= n + len; i++)
    {
        while(head <= tail && i - q[head] > len) ++head;
        ans = max(ans,a[i] + i + a[q[head]] - q[head]);
        while(head <= tail && a[q[tail]] - q[tail] < a[i] - i) --tail;
        q[++tail] = i;
    }
    printf("%lld\n",ans);
    return 0;
}
posted @ 2021-06-24 23:06  luyiming123  阅读(33)  评论(0编辑  收藏  举报