zoukankan      html  css  js  c++  java
  • 清华集训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;
    }
    
    
  • 相关阅读:
    对话框风格的窗口
    对话框
    Notification的功能与用法
    滚动视图(ScrollView)的功能与用法
    css----overflow(布局)
    css----display(显示) 与 Visibility(可见性)
    css----position(定位)
    Vue.js----router(路由)
    HTTP协议-Cookie和Session详解
    MySql 复制表命令
  • 原文地址:https://www.cnblogs.com/Tyher/p/10090299.html
Copyright © 2011-2022 走看看