luoguP3710 方方方的数据结构 KDtree

刚开始看到这道题的时候想的是线段树分治,毕竟这里的乘法和加法都是可以撤销的.   

但是后来发现如果想要线段树分治的话就必须要满足交换律,但是标记 $(x,y)$ ( 乘 $x$ 后加 $y$)只满足结合律,不满足交换律.      

那么就考虑 kdtree.   

kdtree 是维护平面上点的数据结构,然后我们可以把每个询问抽象成点 $(t_{i},x_{i})$ 表示时间轴和坐标轴.     

那么每一次的修改就可以表示成平面上的一个矩阵了,只需在 kdtree 上维护一个标记就行.     

在查询的时候我的做法是暴力跳父亲节点,单次复杂度是 $O($kdtree树高$)$.

code: 

#include <bits/stdc++.h>    
#define N 160000   
#define ll long long 
#define mod 998244353 
#define lson s[now].ch[0] 
#define rson s[now].ch[1]   
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;
int d,n,m; 
int last[N],L[N],R[N],cur[N],GE[N];           
struct Tag 
{
	int x,y;   
	Tag(int a=1,int b=0){ x=a,y=b; }             
	Tag operator+(const Tag b) const 
	{
		Tag c;    
		c.x=(ll)x*b.x%mod;    
		c.y=(ll)((ll)y*b.x%mod+b.y)%mod;    
		return c;   
	}
	void init(int a=1,int b=0) { x=a,y=b; }     
}ope[N];   
struct data 
{
	Tag h;    
	int ch[2],p[2],mi[2],ma[2],f,val,id;                  
	bool operator<(const data b) const 
	{
		return p[d]==b.p[d]?p[d^1]<b.p[d^1]:p[d]<b.p[d];   
	}
	int isin(data b) 
	{
		int fl=1;  
		for(int i=0;i<2;++i) 
			if(mi[i]<b.mi[i]||ma[i]>b.ma[i]) fl=0;
		return fl;   
	}
	int isout(data b) 
	{
		int fl=0;   
		for(int i=0;i<2;++i) 
			if(mi[i]>b.ma[i]||ma[i]<b.mi[i]) fl=1;   
		return fl;    
	}
}s[N];            
void pushup(int x,int y) 
{
	for(int i=0;i<2;++i) 
	{
		s[x].mi[i]=min(s[x].mi[i],s[y].mi[i]);   
		s[x].ma[i]=max(s[x].ma[i],s[y].ma[i]);   
	}
}
int build(int l,int r,int o) 
{  
	int mid=(l+r)>>1;  
	d=o,nth_element(s+l,s+mid,s+1+r);     
	for(int i=0;i<2;++i) 
		s[mid].mi[i]=s[mid].ma[i]=s[mid].p[i]; 
	s[mid].val=0;        
	s[mid].h.init();    
	GE[s[mid].id]=mid;    
	if(mid>l) 
	{
		s[mid].ch[0]=build(l,mid-1,o^1);    
		s[s[mid].ch[0]].f=mid;    
		pushup(mid,s[mid].ch[0]);  
	}
	if(r>mid) 
	{
		s[mid].ch[1]=build(mid+1,r,o^1);    
		s[s[mid].ch[1]].f=mid;   
		pushup(mid,s[mid].ch[1]);   
	}
	return mid;   
}
void mark(int x,Tag v) 
{
	s[x].h=s[x].h+v;     
	s[x].val=(ll)((ll)s[x].val*v.x%mod+v.y)%mod;               
}
void pushdown(int now) 
{
	if(lson) mark(lson,s[now].h);   
	if(rson) mark(rson,s[now].h);  
	s[now].h.init();   
}
void update(int now,data p,Tag v) 
{
	if(s[now].isout(p)) return;    
	if(s[now].isin(p)) 
	{
		mark(now,v);    
		return;    
	}
	if(s[now].p[0]>=p.mi[0]&&s[now].p[0]<=p.ma[0]&&s[now].p[1]>=p.mi[1]&&s[now].p[1]<=p.ma[1]) 
		s[now].val=(ll)((ll)s[now].val*v.x%mod+v.y)%mod;    
	pushdown(now);            
	if(lson) update(lson,p,v);  
	if(rson) update(rson,p,v);    
}                             
int main() 
{
	// setIO("input"); 
	scanf("%d%d",&n,&m); 
	int x,y,z,o,cnt=0;         
	for(int i=1;i<=m;++i) 
	{
		scanf("%d",&o);    
		cur[i]=o;     
		if(o<=2) 
		{
			last[i]=m;  
			scanf("%d%d%d",&L[i],&R[i],&z);      
			if(o==1) ope[i].x=1,ope[i].y=z%mod;   
			if(o==2) ope[i].x=z%mod,ope[i].y=0; 
		}          
		else 
		{ 
			scanf("%d",&x);  
			if(o==3) 
			{
				++cnt;    
				s[cnt].p[0]=i;  
				s[cnt].p[1]=x;     
				s[cnt].id=i;                     
			}
			if(o==4)  last[x]=i-1;        
		}
	}
	data tmp;  
	int root=build(1,cnt,0);     
    for(int i=1;i<=m;++i) 
    {
    	if(cur[i]<=2) 
    	{
    		tmp.mi[0]=i,tmp.ma[0]=last[i];        
    		tmp.mi[1]=L[i],tmp.ma[1]=R[i];         
    		update(root,tmp,ope[i]);         
    	}       
    	if(cur[i]==3) 
    	{
    		x=GE[i];     
    		Tag c(1,0);    
    		for(int j=s[x].f;j;j=s[j].f)    
    			c=c+s[j].h;     
    		printf("%d\n",(ll)((ll)s[x].val*c.x%mod+c.y)%mod);        
    	}
    }
	return 0;
}

  

posted @ 2020-07-06 15:20  EM-LGH  阅读(160)  评论(0编辑  收藏  举报