题目大意:有一个圈上有n个点,编号从 1 ~ n,每个点有一个权值A[i],定义dist(i,j)=min(∣j−i∣,n−∣j−i∣)dist(i,j) = min(|j - i|,n - |j - i|)dist(i,j)=min(∣j−i∣,n−∣j−i∣),问两个点的A[i]+A[j]+dist(i,j)A[i] +A[j] + dist(i,j)A[i]+A[j]+dist(i,j)最大是多少。
题解:先将环断开复制一遍接在原序列的后面,由dist(i,j)dist(i,j)dist(i,j)的定义,当j−i>n2j - i > \frac{n}{2}j−i>2n(这里假设j > i),实际上相当于 j,i+nj,i + nj,i+n 两个点。枚举每一个iii点作为起点,找出i+1,i+n2i + 1,i + \frac{n}{2}i+1,i+2n内的点使得A[i]+A[j]+(j−i)A[i] + A[j] + (j - i)A[i]+A[j]+(j−i)最大,对于枚举的点,A[i]A[i]A[i] 和 iii 已知,只需要维护 范围内最大的 A[j]+jA[j] + jA[j]+j 即可,相当于在一个长度为n2\frac{n}{2}2n的滑块内求最大值,用单调队列使复杂度均摊至O(n)
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
const int maxn = 1e6 + 10;
int h[2 * maxn],n;
int q[2 * maxn],top,rear,ans[2 * maxn],t[2 * maxn];
int main() {
scanf("%d",&n);
for(int i = 1; i <= n; i++) {
scanf("%d",&h[i]);
h[i + n] = h[i];
}
int len = n / 2,res = -0x3f3f3f3f;
for(int i = 1; i <= 2 * n; i++) {
while(top > rear && q[top] <= h[i] + i) top--;
q[++top] = h[i] + i;t[top] = i;
while(rear < top && t[rear + 1] <= i - len) rear++;
if(i > len) {
ans[i - len] = h[i - len] + q[rear + 1] - (i - len);
res = max(res,ans[i - len]);
}
}
printf("%d\n",res);
return 0;
}