Description
已知一个长度为n的序列a1,a2,…,an。
对于每个1<=i<=n,找到最小的非负整数p满足 对于任意的j, aj < = ai + p - sqrt(abs(i-j))
Input
第一行n,(1<=n<=500000)
下面每行一个整数,其中第i行是ai。(0<=ai<=1000000000)
Output
n行,第i行表示对于i,得到的p
Sample Input
6
5
3
2
4
2
4
Sample Output
2
3
5
3
5
4
HINT
传送门
……并不会写。。后面看代码还真是神了。。
题目要求的东西转化一下,其实就是求一个最小的整数p,满足:
aj+abs(i−j)−−−−−−−−√−ai<=p
于是令f(i)为对于i的答案,那么
f(i)=⌈max{aj+abs(i−j)−−−−−−−−√}⌉−ai
考虑一下去掉绝对值,这只要先正着来一遍,再倒着来一遍就好了。
那么假如说是正着来的,也就是假设
j<i
,
对于决策
k<j<i
,j比k优,那么
aj+i−j−−−−√>ak+i−k−−−−√
考虑i后移,比如i->i+1,那么两者变化为:
aj+i+1−j−−−−−−−√和ak+i+1−k−−−−−−−√
变化量各自是
i+1−j−−−−−−−√−i−j−−−−√和i+1−k−−−−−−−√−i−k−−−−√
由于
j>k,
所以
x−j<x−k
,而
f(i)=i√
是一个上凸函数,
所以说变化量里,j的变化量>k的变化量,
所以就是说!k完全没用啦!
随着i的后移,决策点一定是单调移动的……这就是“决策单调性”
涨姿势了。。。啥?泥问我为啥是单调移动的?……
如果j比k优了k就没用了。。如果k比j优那还有点戏。。
所以如果当前决策点为pos,pos及之前的点都没用了,
要么不移动,往后移动的话,pos肯定也没用了……很容易yy的辣!
于是这个显而易见(……划掉)的决策单调性就出来了,
那么对于i,i之前的所有点决策点肯定<=i决策点,
i之后所有点决策点肯定>=i决策点。。
于是就出来了个整体二分的样子。。或者说框架?
就是记录当前决策点区间,还有当前处理的区间,
最坏
T(n)=2∗T(n/2)+O(n),O(nlogn)
这个写法实在是太神了……我队列yy了半年都无果QAQ……
#include<bits/stdc++.h>
using namespace std;
const int
N=500005;
int n,a[N],f[N];
void solve(int L,int R,int l,int r,bool flag){
if (l>r) return;
int pos=0,mid=(l+r)>>1;
if (!flag && mid==1) return;
if (flag && mid==n) return;
double MAX=0.0;
if (!flag){
int t=min(R,mid-1);
for (int i=L;i<=t;i++)
if ((double)sqrt(mid-i)+a[i]>MAX)
MAX=(double)sqrt(mid-i)+a[i],pos=i;
} else{
int t=max(L,mid+1);
for (int i=t;i<=R;i++)
if ((double)sqrt(i-mid)+a[i]>MAX)
MAX=(double)sqrt(i-mid)+a[i],pos=i;
}
f[mid]=max(f[mid],(int)ceil(MAX)-a[mid]);
solve(L,pos,l,mid-1,flag);
solve(pos,R,mid+1,r,flag);
}
int main(){
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
solve(1,n,1,n,0),solve(1,n,1,n,1);
for (int i=1;i<=n;i++) printf("%d\n",f[i]);
return 0;
}