• 清华集训2014 玄学


    • 清华集训2014 玄学
    • 一列对象,常见的区间修改,单点询问,修改操作易于复合,但未必交换以及有逆。
    • 现在询问的时候忽视掉一段前缀与后缀,只留下中间一段连续的修改
    • 强制在线,(nleq 10^5,qleq 6*10^5),保证修改操作不超过(10^5)
    • 二进制分组,猫琨暑假讲过的原题,但是我睡着了。
    • 二进制分组是一种策略,应对询问独立的情况,最基本的二进制分组是这样的:
    • 刚开始没有分组
    • 新来一个修改的时候,对这个修改新建一个分组
    • 如果存在两个大小一样的分组,将它们合并,一直执行直到不存在两个分组大小相等。
    • 通常用线段树实现。
    • 这个模型有很多种等价的变形,比如可以把 (cdq) 分治在线化。
    • 但是我不会啊,到时候可能会补一个二进制分组的学习笔记,咕咕
    • 对于这个题,考虑每一次询问是互相独立的。
    • 那么对修改操作建时间轴线段树,每一次修改就相当于把一个叶子节点的区间搞一下。
    • 对每个节点新建(vector)保存这个节点当前的所有区间。
    • 假设我们已经快速整理出两个儿子的区间信息,按次序合并在父亲了。
    • 那么每一次询问可以定位到线段树上的(log)个区间,按照次序依次产生贡献就可以了。
    • 这个可以直接在(vector)上二分出对应的修改标记。
    • 所以查询是两个(log)的。
    • 现在的问题是怎么把两个儿子快速合并,即把两个(vector)合并成一个。
    • 这里采用的是归并排序的思想。
    • 为了方便,如果两个区间有交,那么就把这两个区间拆成三个区间。
    • 因为每新建一个修改只会产生一个区间,所以总共的端点数不会超过(2*n),是可以接受的。
    • 为了方便,一次修改我们拆开成了三个区间,即右端点分别为(l-1)(r)(n)
    • 这样的好处在于每一个(vector)所拥有的区间拼接起来一定是全集。
    • 还有一个细节就是不能每次修改都(pushup),因为会被不断合并多次,就假了。
    • 可以选择如果一个节点的儿子满了,那么就把他(pushup)
    • 正确性就是,如果一个节点没有被插满,那么他就一定不会被定位到。
    • 因为询问不会询问在这之后的修改操作。
    • 这样的话,修改就只有一个(log)
    • 而每个节点只会被(pushup)一次,所以(pushup)的复杂度是线性的。
    • 总复杂度两个(qlog^2q),而且算法在线。
    • 可以无脑树套树,但是我忘了
    • 疯狂膜拜分块暴切的(cx233666)大聚聚。
    #include<bits/stdc++.h>
    #define R register int
    using namespace std;
    const int N=100001;
    const int M=600000;
    const int T=400001;
    const int Mx=100000;
    int S,n,m,op,l,r,a,b,ans,q,K,tot,w[N];
    struct ip{int r,a,b;}nw;
    vector<ip>G[T];
    int gi(){
        R x=0,k=1;char c=getchar();
        while(c!='-'&&(c<'0'||c>'9'))c=getchar();
        if(c=='-')k=-1,c=getchar();
        while(c<='9'&&c>='0')x=(x<<3)+(x<<1)+c-'0',c=getchar();
        return x*k;
    }
    void upd(R i){
    	R ls=(i<<1),rs=(ls|1),llen=G[ls].size(),rlen=G[rs].size();
    	G[i].clear();
    	for(R p=0,q=0;p<llen&&q<rlen;){
    		R a=1ll*G[ls][p].a*G[rs][q].a%m,b=(1ll*G[rs][q].a*G[ls][p].b+G[rs][q].b)%m;
    		if(G[ls][p].r==G[rs][q].r)G[i].push_back((ip){G[ls][p].r,a,b}),++p,++q;
    		else if(G[ls][p].r<G[rs][q].r)G[i].push_back((ip){G[ls][p].r,a,b}),++p;
    		else G[i].push_back((ip){G[rs][q].r,a,b}),++q;
    	}
    }
    
    int mdf(R Le,R Ri,R i){// tot l r a b
    	if(Le==Ri){
    		if(l!=1)G[i].push_back((ip){l-1,1,0});
    		G[i].push_back((ip){r,a,b});
    		if(r!=n)G[i].push_back((ip){n,1,0});
    		return 1;
    	}
    	R mid=(Le+Ri)>>1,ls=(i<<1),rs=(ls|1);
    	if(tot<=mid){mdf(Le,mid,ls);return 0;}
    	else {
    		R op=mdf(mid+1,Ri,rs);
    		if(op)upd(i);return op;
    	}
    }
    void query(R Le,R Ri,R l,R r,R i){//K
    	if(Le==l&&Ri==r){
    		R le=0,ri=G[i].size()-1,res=-1;
    		while(le<=ri){
    			R Mid=(le+ri)>>1;
    			if(K>G[i][Mid].r)le=Mid+1;
    			else res=Mid,ri=Mid-1;
    		}
    		ans=(1ll*ans*G[i][res].a%m+G[i][res].b)%m;
    		return ;
    	}
    	R mid=(Le+Ri)>>1,ls=(i<<1),rs=(ls|1);
    	if(r<=mid)query(Le,mid,l,r,ls);
    	else if(l>mid)query(mid+1,Ri,l,r,rs);
    	else query(Le,mid,l,mid,ls),query(mid+1,Ri,mid+1,r,rs);
    }
    int main(){
    	S=gi(),n=gi(),m=gi();
    	for(R i=1;i<=n;++i)w[i]=gi();
    	q=gi();
    	for(R t=1;t<=q;++t){
    		op=gi();
    		if(op==1){
    			l=gi(),r=gi(),a=gi()%m,b=gi()%m,++tot;
    			if(S&1)l^=ans,r^=ans;
    			mdf(1,Mx,1);//l r a b;
    		}
    		else {
    			l=gi(),r=gi(),K=gi();if(S&1)l^=ans,r^=ans,K^=ans;
    			ans=w[K],query(1,Mx,l,r,1);
    			printf("%d
    ",ans);
    		}
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    PHP基本的语法以及和Java的差别
    Linux 性能測试工具
    【Oracle 集群】Linux下Oracle RAC集群搭建之Oracle DataBase安装(八)
    【Oracle 集群】Oracle 11G RAC教程之集群安装(七)
    【Oracle 集群】11G RAC 知识图文详细教程之RAC在LINUX上使用NFS安装前准备(六)
    【Oracle 集群】ORACLE DATABASE 11G RAC 知识图文详细教程之RAC 特殊问题和实战经验(五)
    【Oracle 集群】ORACLE DATABASE 11G RAC 知识图文详细教程之缓存融合技术和主要后台进程(四)
    【Oracle 集群】ORACLE DATABASE 11G RAC 知识图文详细教程之RAC 工作原理和相关组件(三)
    Oracle 集群】ORACLE DATABASE 11G RAC 知识图文详细教程之ORACLE集群概念和原理(二)
    【Oracle 集群】ORACLE DATABASE 11G RAC 知识图文详细教程之集群概念介绍(一)
  • 原文地址:https://www.cnblogs.com/Tyher/p/10090299.html
Copyright © 2020-2023  润新知