一、题目
二、解法
宋队写矩阵直接 \(\tt T\) 飞了,这么多操作好像势能线段树也很难\(...\)
\(\tt observation\):对于最难搞的二操作,新的值是关于原来值的只有一个折点的折线函数。
那么我们尝试维护函数 \(f(x)=\max(x+a,b)\),对于前三种操作,可以写出它们的函数:
- 区间加操作:\(f(x)=\{x_i,-inf\}\)
- 区间减操作:\(f(x)=\{-x_i,0\}\)
- 区间赋值操作:\(f(x)=\{-inf,x_i\}\)
然后我们 \(\tt DIY\) 一下这个函数的运算法则就行了,首先考虑标记的合并:
\[\max(\max(x+a,b)+c,d)=\max(x+a+c,\max(b+c,d))
\]
发现合并函数是和 \(x\) 无关的,所以这东西满足结合律,可以类似普通标记下传。
然后考虑怎么维护区间历史最大值,我们考虑两个函数 \(f_1(x)=\{a,b\},f_2(x)=\{c,d\}\) 如何取最值:
\[\max(\max(x+a,b),\max(x+c,d))=\max(x+\max(a,b),\max(c,d))
\]
所以再维护 \(g(x)\) 表示操作序列的历史最大值,用自己的 \(f(x)\) 和传下来的 \(g‘(x)\) 合并再用上面的运算法则更新它即可,最后你发现这和加法的历史最大值做法其实差不多,时间复杂度 \(O(n\log n)\)
三、总结
如果难搞的操作只有一个,可以思考它的特点再考虑怎么维护,本题就是以他为中心自己 \(\tt DIY\) 了函数及其运算法则。
#include <cstdio>
#include <iostream>
using namespace std;
const int M = 500005;
#define int long long
const int inf = 1e15;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<‘0‘ || c>‘9‘) {if(c==‘-‘) f=-1;}
while(c>=‘0‘ && c<=‘9‘) {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,a[M];
struct node
{
int x,y;
node(int X=0,int Y=0)
{
x=X;y=Y;
if(x<-inf) x=-inf;
if(y<-inf) y=-inf;
}
void clear() {x=y=0;}
node operator + (const node &b) const
{
return node(max(x,b.x),max(y,b.y));
}
node operator * (const node &b) const
{
return node(x+b.x,max(y+b.x,b.y));
}
}mx[4*M],hx[4*M];
void fuck(int i,node a,node b)
{
hx[i]=hx[i]+(mx[i]*b);
mx[i]=mx[i]*a;
}
void down(int i)
{
fuck(i<<1,mx[i],hx[i]);
fuck(i<<1|1,mx[i],hx[i]);
mx[i].clear();hx[i].clear();
}
node con(int op,int c)
{
if(op==1) return node(c,-inf);
if(op==2) return node(-c,0);
if(op==3) return node(-inf,c);
return node(0,0);
}
void upd(int i,int l,int r,int L,int R,node x)
{
if(L>r || l>R) return ;
if(L<=l && r<=R)
{
fuck(i,x,x);
return ;
}
int mid=(l+r)>>1;down(i);
upd(i<<1,l,mid,L,R,x);
upd(i<<1|1,mid+1,r,L,R,x);
}
int ask(int i,int l,int r,int id)
{
if(l==r) return max(mx[i].x+a[l],mx[i].y);
int mid=(l+r)>>1;down(i);
if(mid>=id) return ask(i<<1,l,mid,id);
return ask(i<<1|1,mid+1,r,id);
}
int hask(int i,int l,int r,int id)
{
if(l==r) return max(hx[i].x+a[l],hx[i].y);
int mid=(l+r)>>1;down(i);
if(mid>=id) return hask(i<<1,l,mid,id);
return hask(i<<1|1,mid+1,r,id);
}
signed main()
{
n=read();m=read();
for(int i=1;i<=n;i++)
a[i]=read();
while(m--)
{
int op=read();
if(op==4)
printf("%lld\n",ask(1,1,n,read()));
else if(op==5)
printf("%lld\n",hask(1,1,n,read()));
else
{
int l=read(),r=read(),c=read();
node t=con(op,c);
upd(1,1,n,l,r,t);
}
}
}