题目大意:给定一个序列
ai
,对于每一个
i
求
看了题解才知道是决策单调性。。。
那我这个做法可以算是乱搞了?
(似乎这个做法也可以拓展到所有满足决策单调性的1D1D上?)
显然我们可以做两遍,第一遍只考虑
j<i
,第二遍只考虑
j>i
首先根号这东西有个性质。。。
n−√
的增长速度随
n
的增大而减小。。(其实就是函数上凸)
这意味着什么呢?这意味着对于某对
但是如果
那么我们可以开个set维护一个单调递减的决策队列。。。
对于决策队列中的每对点
(j,k)
,二分找到
k
艹掉
然后DP的时候枚举
i
,先维护一下单调队列,然后处理发生在这一时刻的事件
感觉这个写法确实可以用于所有决策单调。。。新技能get√
#include <set>
#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 500500
using namespace std;
int n,a[M],_sqrt[M];
int f[M],g[M];
set<int> q;
vector<pair<int,int> > s[M];
int Bisection(int x,int y)//二分x在什么时候被y超过
{
int l=y+1,r=n+1;
while(r-l>1)
{
int mid=l+r>>1;
if( a[x]+sqrt(mid-x) < a[y]+sqrt(mid-y) )
r=mid;
else
l=mid;
}
return a[x]+sqrt(l-x) < a[y]+sqrt(l-y) ? l : r ;
}
void DP(int f[])
{
int i;
for(i=1;i<=n;i++)
{
if( *q.begin()!=5 )
++i,--i;
while( !q.empty() )
{
set<int>::iterator it=q.end();--it;
if( a[*it]+sqrt(i-*it) < a[i] )
q.erase(it);
else
break;
}
if( !q.empty() )
{
set<int>::iterator it=q.end();--it;
s[Bisection(*it,i)].push_back(make_pair(*it,i));
}
q.insert(i);
while( !s[i].empty() )
{
int x=s[i].back().first,y=s[i].back().second;
s[i].pop_back();
if( q.find(y)==q.end() )
continue;
set<int>::iterator it=q.find(x);
if( it==q.end() )
continue;
while(1)
{
if( a[*it]+sqrt(i-*it) < a[y]+sqrt(i-y) )
{
if( it==q.begin() )
{
q.erase(it);
break;
}
set<int>::iterator temp=it;it--;
q.erase(temp);
}
else
{
s[Bisection(*it,y)].push_back(make_pair(*it,y));
break;
}
}
}
set<int>::iterator it=q.begin();
f[i]=max(f[i],a[*it]+_sqrt[i-*it]-a[i]);
}
}
int main()
{
//freopen("pio.in","r",stdin);
//freopen("pio.out","w",stdout);
int i,j;
cin>>n;
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
for(i=0;i*i<=n;i++)
for(j=i*i+1;j<=(i+1)*(i+1)&&j<=n;j++)
_sqrt[j]=i+1;
DP(f);
q.clear();
for(i=1;i<=n+1;i++)
s[i].clear();
for(i=1;i<=n>>1;i++)
swap(a[i],a[n-i+1]);
DP(g);
for(i=1;i<=n;i++)
printf("%d\n",max(f[i],g[n-i+1]));
return 0;
}