清华集训2014 玄学

  • 清华集训2014 玄学
  • 一列对象,常见的区间修改,单点询问,修改操作易于复合,但未必交换以及有逆。
  • 现在询问的时候忽视掉一段前缀与后缀,只留下中间一段连续的修改
  • 强制在线,\(n\leq 10^5,q\leq 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\n",ans);
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Tyher/p/10090299.html