[UOJ 164] V

一、题目

点此看题

二、解法

宋队写矩阵直接 \(\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);
		}
	}
}

[UOJ 164] V

上一篇:[UOJ50] 链式反应


下一篇:8.24-轨迹