真心好难,代码不好写。
最后算是抄的clairs的代码。
考虑分块,每块维护两个标记 ts,td ts,td。
那么对于块中一个位置 i i,它的实际值为 i×td+ts+vi i×td+ts+vi。
修改的时候,对于整块,直接打标记,对于零散的暴力修改,然后重构凸壳,时间复杂度 O(n√) O(n)。
查询的时候在凸壳上二分即可,时间复杂度 O(n√logn) O(nlogn)。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=120006;
const ll inf=1ll<<60;
inline ll read()
{
ll ans,f=1;char ch;
while((ch=getchar())<'0'||ch>'9') if (ch=='-') f=-1;ans=ch-'0';
while((ch=getchar())>='0'&&ch<='9') ans=ans*10+ch-'0';
return ans*f;
}
int n,m,block,num,pos[N],st[N],en[N];
ll sum[N];
ll *v=sum;
double xielv(int i,int j)
{
return (double)(sum[i]-sum[j])/(double)(j-i);
}
struct aa
{
int q[605],tot;
ll add,tag;
ll query()
{
int mid,lt=2,rt=tot,ans=q[1];
while (lt<=rt)
{
mid=(lt+rt)>>1;
if (xielv(q[mid-1],q[mid])<tag) ans=q[mid],lt=mid+1;
else rt=mid-1;
}
return sum[ans]+tag*ans+add;
}//ok
}g[605];
void build(int x)
{
int &tot=g[x].tot;tot=0;
for (int i=st[x];i<=en[x];i++)
{
while (tot>=2 &&
xielv(g[x].q[tot-1],g[x].q[tot])>=xielv(g[x].q[tot],i)
) tot--;
g[x].q[++tot]=i;
}
}//ok
void init()
{
n=read();
ll x;
block=sqrt(n);
num=block+(block*block!=n);
for (int i=1;i<=n;i++) pos[i]=(i-1)/block+1;
for(int i=1;i<=n;i++)en[pos[i]]=i;
for(int i=n;i;i--)st[pos[i]]=i;
for (int i=1;i<=n;i++)
{
x=read();
sum[i]=x+sum[i-1];
}
for (int i=1;i<=num;i++) build(i);
}//ok
inline void change(int x,int y,ll s,ll d){
if(pos[x]==pos[y]){
for(int i=x;i<=y;i++)v[i]+=s+d*i;
build(pos[x]);
return;
}
for(int i=pos[x]+1;i<pos[y];i++)g[i].add+=s,g[i].tag+=d;
for(int i=en[pos[x]];i>=x;i--)v[i]+=s+d*i;
build(pos[x]);
for(int i=st[pos[y]];i<=y;i++)v[i]+=s+d*i;
build(pos[y]);
}
inline void updata(){
int x,y;ll z;
x=read(),y=read(),z=read();
change(x,y,z*(1-x),z);
if(y<n)change(y+1,n,z*(y-x+1),0);
}
inline void up(ll&a,ll b){if(a<b)a=b;}
inline ll query(){
int x=read(),y=read();
ll t=-(1LL<<62);
if(pos[x]==pos[y]){
for(int i=x;i<=y;i++)up(t,g[pos[i]].tag*i+g[pos[i]].add+v[i]);
return t;
}
for(int i=pos[x]+1;i<pos[y];i++)up(t,g[i].query());
for(int i=en[pos[x]];i>=x;i--)up(t,g[pos[i]].tag*i+g[pos[i]].add+v[i]);
for(int i=st[pos[y]];i<=y;i++)up(t,g[pos[i]].tag*i+g[pos[i]].add+v[i]);
return t;
}
int main()
{
init();
int op;m=read();
while (m--)
{
op=read();
if (op==0) updata();
else printf("%lld\n",query());
}
return 0;
}
总结
1:注意变量等的命名,不要出现重复,例如ll类型和ll左端点不要重复。
2:这里用凸包,其实就是类似于斜率优化的式子,(就是设i<j,j优于i这种情况,推出的式子)也包括那些性质。
3:凸包就是在这些不能一维表示,然后须用二维,但是二维难以取区间最优值,就转化为凸包来解二维(有两个和i有关量的乘号)求。
4:此题思路须再整理!再分析!(今天太晚了。。)