玄学[UOJ46][线段树]

文章目录

题目

UOJ
在这里插入图片描述
强制在线

思路

在这里插入图片描述
定义运算为 a x + b ax+b ,建立以时间为下标的线段树
则左右儿子,分别为 a x + b ax+b c x + d cx+d
上传标记后为 c ( a x + b ) + d c(ax+b)+d
a c x + ( b c + d ) acx+(bc+d)
1.非强制在线
可将询问按 a k a_k 排序后拆分线段扫描线,查询直接线段树上查即可
时间复杂度 O ( n l o g n ) O(nlogn)
2.强制在线
线段树如上,但对于线段树一个节点 [ L , R ] [L,R] 代表维护进行 L R L\sim R
修改操作后整个序列对应运算
但是如果直接维护空间是 O ( n 2 ) O(n^2) 级别的
我们可以维护连续相同的运算
合并左右儿子用类似归并的方法
发现如果节点代表时间区间长度为 L L 那么空间开销为 L L 数量级,于是整个线段树空间开销为 n l o g n nlogn
查询操作需要二分出位置在哪一段 单次, O ( l o g 2 n ) O(log^2n)
修改不能按照常规操作会退化成 单次 O ( n ) O(n) ,很巧妙,对于 t t 时刻只对于节点右端点不大于 t t 的区间进行上传操作均摊 O ( n l o g n ) O(nlogn)
于是总时间复杂度为 O ( n l o g 2 n ) O(nlog^2n)

代码

#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<cstring>
#include<climits>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
//#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
//char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
inline int read() {
	bool f=0;int x=0;char c=getchar();
	while(c<'0'||'9'<c){if(c==EOF)exit(0);if(c=='-')f=1;c=getchar();}
	while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return !f?x:-x;
}
#define lch (u<<1)
#define rch (u<<1|1)
#define MAXN 600000
#define INF 0x3f3f3f3f
struct Tran{
	int r,a,b;
}node[20*MAXN+5];
int n,Mod,num[MAXN+5];
int ncnt,L[5*MAXN+5],R[5*MAXN+5];
void PushUp(int u){
	L[u]=ncnt+1;
	int lsiz=R[lch],rsiz=R[rch],p=L[lch],q=L[rch];
	while(p<=lsiz&&q<=rsiz){
		node[++ncnt]=(Tran){min(node[p].r,node[q].r),1ll*node[p].a*node[q].a%Mod,(1ll*node[p].b*node[q].a%Mod+node[q].b)%Mod};
		if(node[p].r<node[q].r) p++;
		else if(node[p].r>node[q].r) q++;
		else p++,q++;
	}
	while(p<=lsiz) node[++ncnt]=node[p++];
	while(q<=rsiz) node[++ncnt]=node[q++];
	R[u]=ncnt;
	return ;
}
int a,b;
void Insert(int u,int tL,int tR,int pL,int pR,int tp){
	if(tL==tR){
		L[u]=ncnt+1;
		if(pL>1) node[++ncnt]=(Tran){pL-1,1,0};
		node[++ncnt]=(Tran){pR,a,b};
		if(pR<n) node[++ncnt]=(Tran){n,1,0};
		R[u]=ncnt;
		return ;
	}
	int Mid=(tL+tR)>>1;
	if(tp<=Mid) 
		Insert(lch,tL,Mid,pL,pR,tp);
	else Insert(rch,Mid+1,tR,pL,pR,tp);
	if(tp==tR) 
		PushUp(u);
	return ;
}
int A,B;
void BS(int u,int p){
	int l=L[u]-1,r=R[u];
	while(l+1<r){
		int mid=(l+r)>>1;
		if(p<=node[mid].r) r=mid;
		else l=mid;
	}
	A=1ll*A*node[r].a%Mod,B=(1ll*B*node[r].a%Mod+node[r].b)%Mod;
	return ;
}
void Query(int u,int tL,int tR,int qL,int qR,int p){
	if(qL<=tL&&tR<=qR){
		BS(u,p);
		return ;
	}
	int Mid=(tL+tR)>>1;
	if(qL<=Mid) Query(lch,tL,Mid,qL,qR,p);
	if(Mid+1<=qR) Query(rch,Mid+1,tR,qL,qR,p);
	return ;
}
int main(){
	int kind=read()&1,lastans=0,qcnt=0;
	n=read(),Mod=read();
	for(int i=1;i<=n;i++)
		num[i]=read();
	int q=read();
	for(int t=1;t<=q;t++){
		int opt=read();
		if(opt==1){
			int i=read(),j=read();a=read(),b=read();
			if(kind) i^=lastans,j^=lastans;
			Insert(1,1,q,i,j,++qcnt);
		}
		else{
			int i=read(),j=read(),k=read();
			if(kind) i^=lastans,j^=lastans,k^=lastans;
			A=1,B=0;
			Query(1,1,q,i,j,k);
			lastans=(1ll*A*num[k]%Mod+B)%Mod;
			printf("%d\n",lastans);
		}
	}
	return 0;
}

思考

时间+区间修改+离线要多联系线段树中扫描线技巧,强制在线想办法优化空间

猜你喜欢

转载自blog.csdn.net/qq_37555704/article/details/104083332