Description
OIVillage是一个风景秀美的乡村,为了更好的利用当地的旅游资源,吸引游客,推动经济发展,xkszltl决定修建了一条铁路将当地n个最著名的经典连接起来,让游客可以通过火车从铁路起点(1号景点)出发,依次游览每个景区。为了更好的评价这条铁路,xkszltl为每一个景区都哦赋予了一个美观度,而一条旅行路径的价值就是它所经过的景区的美观度之和。不过,随着天气与季节的变化,某些景点的美观度也会发生变化。
xkszltl希望为每位旅客提供最佳的旅行指导,但是由于游客的时间有限,不一定能游览全部景区,然而他们也不希望旅途过于短暂,所以每个游客都希望能在某一个区间内的车站结束旅程,而xkszltl的任务就是为他们选择一个终点使得旅行线路的价值最大。可是当地的景点与前来观光的旅客实在是太多了,xkszltl无法及时完成任务,于是找到了准备虐杀NOI2011的你,希望你能帮助他完成这个艰巨的任务。
Input
第一行给出一个整数n,接下来一行给出n的景区的初始美观度。
第三行给出一个整数m,接下来m行每行为一条指令:
1.
0 x y k:表示将x到y这段铁路边上的景区的美观度加上k;
2.
1 x y:表示有一名旅客想要在x到y这段(含x与y)中的某一站下车,你需要告诉他最大的旅行价值。
Output
对于每个询问,输出一个整数表示最大的旅行价值。
Sample Input
5
1 8 -8 3 -7
3
1 1 5
0 1 3 6
1 2 4
1 8 -8 3 -7
3
1 1 5
0 1 3 6
1 2 4
Sample Output
9
22
22
HINT
Data Limit:
对于20%的数据,n,m≤3000;
对于40%的数据,n,m≤30000;
对于50%的数据,n,m≤50000;
另外20%的数据,n,m≤100000,修改操作≤20;
对于100%的数据,n,m≤100000。
题解:
设s[i]为i位置美观度的前缀和.
把(i,s[i])看成一个点,考虑分块.
对于每一块,维护一个上凸壳,对于整块的询问就可以在凸壳上二分.
修改的时候可以发现是加一个等差数列,所以对于整块的可以直接打标记.
边缘的部分暴力修改然后暴力重构凸壳.
对于每次修改的右端点之后的块,等于是整块加一个值,
这样最优值的位置是不变的,所以直接打标记即可.
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define N 100010
#define M 400
#define ll long long
#define inf 1LL<<60
using namespace std;
int bk,n,m,cnt,kind,x,y,bl[N],l[M],r[M],st[N],xu[M][M],size[M];
ll s[N],f[M],d[M],c[M],k,temp;
int read(){
int x(0),f(1);char ch=getchar();
while (ch<'0'||ch>'9') { if (ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
void getbk(){
bk=sqrt(n);
if (n%bk) cnt=n/bk+1;
else cnt=n/bk;
for (int i=1;i<=n;i++)
bl[i]=(i-1)/bk+1;
for (int i=1;i<=cnt;i++){
l[i]=(i-1)*bk+1;
r[i]=min(n,bk*i);
}
}
double cal(int x,int y){
return (double)(s[y]-s[x])/(double)(y-x);
}
void build(int x){
int top=0;
st[++top]=l[x];
for (int i=l[x]+1;i<=r[x];i++){
while (top>1&&cal(st[top-1],st[top])<cal(st[top-1],i)) top--;
st[++top]=i;
}
st[0]=0;st[top+1]=n+1;size[x]=top;
for (int i=0;i<=top+1;i++) xu[x][i]=st[i];
}
void pushdown(int x){
temp=f[x];
for (int i=l[x];i<=r[x];i++){
s[i]+=temp;temp+=d[x];s[i]+=c[x];
}
f[x]=c[x]=d[x]=0;
}
void change(int x,int y,ll k){
int a=bl[x],b=bl[y];
temp=k*(l[a+1]-x+1);
for (int i=a+1;i<b;i++){
f[i]+=temp;d[i]+=k;
temp+=k*(r[i]-l[i]+1);
}
pushdown(a);temp=k;
for (int i=x;i<=min(y,r[a]);i++){
s[i]+=temp;temp+=k;
}
build(a);pushdown(b);
if (a!=b){
temp=k*(l[b]-x+1);
for (int i=l[b];i<=y;i++){
s[i]+=temp;temp+=k;
}
}
temp=k*(y-x+1);
for (int i=y+1;i<=r[b];i++)
s[i]+=temp;
build(b);
for (int i=b+1;i<=cnt;i++)
c[i]+=temp;
}
ll getval(int x){
ll ans(0);
if (x==0||x==n+1) return -inf;
int t=bl[x];
ans=s[x]+c[t];
ans+=(x-l[t])*d[t]+f[t];
return ans;
}
ll ask(int x){
int a=1,b=size[x];
while (a<=b){
int mid=(a+b)>>1;
ll t1=getval(xu[x][mid-1]);
ll t2=getval(xu[x][mid]);
ll t3=getval(xu[x][mid+1]);
if (t1<t2&&t2<t3) a=mid+1;
else if (t3<t2&&t2<t1) b=mid-1;
else return t2;
}
}
ll query(int x,int y){
int a=bl[x],b=bl[y];
ll ans=-inf;
for (int i=a+1;i<b;i++)
ans=max(ans,ask(i));
for (int i=x;i<=min(y,r[a]);i++)
ans=max(ans,getval(i));
if (a!=b){
for (int i=l[b];i<=y;i++)
ans=max(ans,getval(i));
}
return ans;
}
int main(){
n=read();
for (int i=1;i<=n;i++){
x=read();
s[i]=s[i-1]+x;
}
getbk();
for (int i=1;i<=cnt;i++) build(i);
m=read();
for (int i=1;i<=m;i++){
kind=read();x=read();y=read();
if (kind==0){k=read();change(x,y,k);}
else printf("%lld\n",query(x,y));
}
}