1. 程式人生 > >清華集訓2014 玄學

清華集訓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;
}