题解
10^5的数据,常规做法理所当然的T了QAQ
看了一下别的大佬的博客,解法是 单调队列,
能用单调队列,说明存在单调的性质,
这里是距离的单调,单调递减,队列里能加进来的点,如果其作为起点一定是不可以比前一个点作为起点产生的权值大
emmmm也不知道可不可以这么说↑
假设新加入的点为i,队列最后一个点为ed,h[i] 一定小于等于 h[ed]+dis(i,ed),否则,统计最大值,i点作为起点一定比ed作为起点更优(高度又高,且更易存在半径长度的区间内),应该直接放弃ed点
接下来是大致做法:
先将圆拆成线段,复制前面的数据,再多补一倍的区间,记作 h[1~2*n],
选用双端队列维护长度最大为半径大小(
n
2
\frac{n}{2}
2n)的区间,(因为涉及到取第一个点和放弃最后一个点,当然数组万能)
设当前遍历到第i个点作为终点,
从队列里依次拿出一个点作为起点,如果两个端点距离超过半径,就pop这个起点,(超过半径了,说明反方向绕路更短,不用担心漏掉,我们补了一倍的区间呢),
如果在半径范围内,更新这个区间内最远的距离,
然后将当前点作为潜在的起点,和前面的点比较一下,清理一下队列末端的点,再加入到队列当中
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10;
int n,m,r,id1,id2;
ll h[N],ans;
void update(int x,int y){
ll tmp=h[x]+h[y]+(y-x)*r;
x%=n/2,y%=n/2;//还原坐标
if(x>y)swap(x,y);
if(tmp>ans){
ans=tmp;
id1=x;id2=y;
}else if(tmp==ans && (x<id1 || (x==id1 && y<id2 ))){
id1=x;id2=y;
}
}
void solve(){
deque<int>q;//用双端队列维护长度为半径大小的区间
q.push_back(0);
for (int i = 1; i < n; ++i) {//遍历端点
while(!q.empty() && i-q.front()>m) //如果以队列第一个点位起点 当前点为终点 区间长度超过m 放弃
q.pop_front();
update(q.front(),i);//更新答案
while(!q.empty() && h[q.back()]+(i-q.back())*r < h[i]) q.pop_back();
q.push_back(i);
}
}
int main(){
ios::sync_with_stdio(0);
int T;
cin>>T;
for (int cs = 1; cs <= T; ++cs) {
printf("Case #%d:\n",cs);
cin>>n>>r;
for (int i = 0; i < n; ++i) {
cin>>h[i];
h[i+n]=h[i];
}
m=n/2;//半径
n+=n;
ans=0;
solve();//单调队列
printf("%d %d\n", id1+1,id2+1);
}
return 0;
}