[POI2011]Lightning Conductor

题面在这里

description

已知一个长度为\(n\)的序列\(a_1,a_2,...,a_n\)

对于每个\(1\le i\le n\),找到最小的非负整数\(p\),
满足对于任意的\(1\le j\le n\),\(a_j\le a_i+p-\sqrt{|i-j|}\)

data range

\[n\le 5\times 10^5,a_i\le 10^9 \]

solution

绝对值怎么办?
我们先从左到右\(DP\ j< i\)的部分(此时有\(|i-j|=i-j\)),
再右到左\(DP\ j> i\)的部分(此时有\(|i-j|=j-i\));

那么我们现在考虑这个式子:

\[f[i]=max_{j=1}^{i-1}(a[j]-a[i]+\sqrt{i-j}) \]

对于每一个相同的\(i\),
\(w[j]=\sqrt{i-j}\),那么我们很容易发现这个函数是凸函数
对于两个决策\(j<k\),当\((j-k)\)一定时,我们发现\(w[j]-w[k]\)随着\(i\)的递增而递减
而对于这两个决策,\(j\)\(k\)优当且仅当对于\(j<k<i\le n\),

\[a[j]+\sqrt{i-j}\ge a[k]+\sqrt{i-k} \]

\[g[j,k]=a[j]-a[k]+w[j]-w[k]\ge 0 \]

\(i\)递增时,\(g[j,k]\)这个函数单调递减,那么存在一个分界点\(i'\),
使得当\(i\le i'\)时,\(g[j,k]\ge 0\)(\(j\)更优),当\(i\ge i'\)时,\(g[j,k]\le 0\)(\(k\)更优);

那么我们知道这个\(DP\)方程具有决策单调性
具体如何实现?

考虑相邻的两个决策\(j,k\),
由于上面的性质,我们可以二分出这两个决策的分界点\(t[j,k]\);
对于相邻的三个决策\(x,y,z\),如果\(t[x,y]\ge t[y,z]\)那么决策\(y\)是没有用的可以扔掉

考虑维护一个单调队列,存放所有合法的决策;
由于我们在维护的同时\(i\)是变化的,
因此我们需要求出每个决策此时转移最优的区间
即单调队列存放的元素为\((pos,l_{pos},r_{pos})\)(注意\(pos < l_{pos}\le r_{pos}\))

当加入新决策\(j\)时,考虑队尾的决策\((k,l_k,r_k)\);
如果\(j\)总是比\(k\)要劣(在这里指对于\(i=n\)仍有\(a[j]+\sqrt{i-j}\le a[k]+\sqrt{i-k}\)),
则不考虑决策\(j\);
否则(此时\(r_j=n\)),如果对于\(i=l_k\)\(a[j]+\sqrt{i-j}\ge a[k]+\sqrt{i-k}\),
一直弹掉队尾决策并考虑队尾的前一个决策,如果队列为空则压入\((j,j,n)\);
否则(此时存在一个决策\(k\)无法被弹出),考虑二分决策\(k\)\(j\)的分界点\(mid\)
(本人的定义是,对于\(i\le mid\),有\(a[k]+\sqrt{i-k}> a[j]+\sqrt{i-j}\)),
之后将\(r_k\)修改为\(mid\),压入决策\((j,mid,n)\)

il dd calc(int j,int i){return a[j]-a[i]+sqrt(i-j);}
//计算j转移至i时对应的DP值
il int divide(int k,int j,int l,int r){
	RG int mid,ans=l;
	while(l<=r){
		mid=(l+r)>>1;
		if(calc(k,mid)>calc(j,mid))
			ans=mid,l=mid+1;
		else r=mid-1;
	}
	return ans;
}
//二分分界点
il void push(int i){
	if(calc(Qx[R],n)>calc(i,n))return;
	while(calc(Qx[R],Ql[R])<calc(i,Ql[R])&&L<=R)R--;
	if(L>R){Ql[++R]=i;Qr[R]=n;Qx[R]=i;return;}
	Qr[R]=divide(Qx[R],i,Ql[R],Qr[R]);
	R++;Ql[R]=Qr[R-1]+1;Qr[R]=n;Qx[R]=i;
}
//插入新决策

当要查询位置\(pos\)时,直接从队头开始查找,
如果队首决策\((k,l_k,r_k)\)\(r_k<pos\)或队首决策劣于队首后决策则将其弹出;
否则,将其作为此次查询的答案

il int pop(int i){
	while(Qr[L]<i||(L<R&&calc(Qx[L],i)<calc(Qx[L+1],i)))L++;
	return Qx[L];
}
//查询答案

这里是总的\(DP\)模板

il void DP(int *a,int *p,int n){
	L=0;R=-1;
	for(RG int i=1;i<=n;i++){
		if(i!=1)p[i]=ceil(calc(pop(i),i));Ql[L]=i;push(i);
		//答案为非负整数,这里做了上取整
	}
}
//DP序列

code

#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<iomanip>
#include<cstring>
#include<complex>
#include<vector>
#include<cstdio>
#include<string>
#include<bitset>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define mp make_pair
#define pub push_back
#define puf push_front
#define pob pop_back
#define pof pop_front
#define RG register
#define il inline
using namespace std;
typedef unsigned long long ull;
typedef vector<int>VI;
typedef long long ll;
typedef double dd;
const dd eps=1e-10;
const int N=1000000;
const int inf=1e9+7;
il ll read(){
	RG ll data=0,w=1;RG char ch=getchar();
	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
	if(ch=='-')w=-1,ch=getchar();
	while(ch<='9'&&ch>='0')data=data*10+ch-48,ch=getchar();
	return data*w;
}

il void file(){
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
}

int n,a[N],p[N],s[N];
int Qx[N],Ql[N],Qr[N],L,R;

il dd calc(int j,int i){return a[j]-a[i]+sqrt(i-j);}
//计算j转移至i时对应的DP值
il int divide(int k,int j,int l,int r){
	RG int mid,ans=l;
	while(l<=r){
		mid=(l+r)>>1;
		if(calc(k,mid)>calc(j,mid))
			ans=mid,l=mid+1;
		else r=mid-1;
	}
	return ans;
}
//二分分界点
il void push(int i){
	if(calc(Qx[R],n)>calc(i,n))return;
	while(calc(Qx[R],Ql[R])<calc(i,Ql[R])&&L<=R)R--;
	if(L>R){Ql[++R]=i;Qr[R]=n;Qx[R]=i;return;}
	Qr[R]=divide(Qx[R],i,Ql[R],Qr[R]);
	R++;Ql[R]=Qr[R-1]+1;Qr[R]=n;Qx[R]=i;
}
//插入新决策
il int pop(int i){
	while(Qr[L]<i||(L<R&&calc(Qx[L],i)<calc(Qx[L+1],i)))L++;
	return Qx[L];
}
//查询答案
il void DP(int *a,int *p,int n){
	L=0;R=-1;
	for(RG int i=1;i<=n;i++){
		if(i!=1)p[i]=ceil(calc(pop(i),i));Ql[L]=i;push(i);
	}
}
//DP序列
int main()
{
	n=read();
	for(RG int i=1;i<=n;i++)a[i]=read();
	DP(a,p,n);reverse(a+1,a+1+n);DP(a,s,n);
	for(RG int i=1;i<=n;i++)
		printf("%d\n",max(0,max(p[i],s[n-i+1])));
	//记得答案是非负整数
	return 0;
}

posted @ 2018-04-17 21:13  cjfdf  阅读(130)  评论(0编辑  收藏  举报