cdq

转移自老blog

cdq

三维偏序

#include<bits/stdc++.h>
using namespace std;

const int maxn= 2e5+5;
struct node{int x,y,z,w,ct;}a[maxn],b[maxn];
int ans[maxn],BIT[maxn],B;

bool cmp(node a,node b){
    if(a.x!=b.x)return a.x<b.x;
    if(a.y!=b.y)return a.y<b.y;
    return a.z<b.z;
}

bool equal(node a,node b){
    return a.x==b.x&&a.y==b.y&&a.z==b.z;
}

void add(int i,int d){
    for(;i<=B;i+=i&-i) BIT[i]+=d;
}
int sum(int i){
    int ret=0;
    for(;i>=1;i-=i&-i) ret+=BIT[i];
    return ret;
}

void cdq(int l,int r){
    if(l==r) return;
    int mid=l+r>>1;
    cdq(l,mid), cdq(mid+1,r);
    int u=l,v=mid+1;
    for(int i=l;i<=r;i++) {
        if(v>r||(u<=mid&&a[u].y<=a[v].y)){
            b[i]=a[u++];
            add(b[i].z,b[i].ct);
        }
        else{
            b[i]=a[v++];
            b[i].w+=sum(b[i].z);
        }
    }
    for(int i=l;i<=mid;i++) add(a[i].z,-a[i].ct);
    for(int i=l;i<=r;i++) a[i]=b[i];
}

int main(){
    int n,k;
    scanf("%d%d",&n,&k);
    B=k;
    for(int i=1;i<=n;i++) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
    sort(a+1,a+1+n,cmp);

    int ed=0; a[ed].x=-1;
    for(int i=1;i<=n;i++){
        if(!equal(a[i],a[ed])) a[++ed]=a[i];
        a[ed].ct++;
    }
    cdq(1,ed);
    for(int i=1;i<=ed;i++) ans[a[i].w+a[i].ct]+=a[i].ct;
    for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
}

四维偏序- cdq分治降低三维+排序一维

hdu5126
#include<bits/stdc++.h>
using namespace std;

const int maxq=5e4+5;

struct node{
    int w[3],id,ct,type;
    node()= default;
    node(int x,int y,int z,int id,int ct,int type):id(id),ct(ct),type(type){
        w[0]=x;
        w[1]=y;
        w[2]=z;
    }
};

int cmpd;
bool sleqt(node a,node b){
    for(int i=3-cmpd;i<3;i++) {
        if(a.w[i]!=b.w[i]) return a.w[i]<b.w[i];
    }
    return a.type<=b.type;
}

node a[maxq<<3];
int ans[maxq<<3];

int merge(node*a,int n,node*b,int d,int flag){  //assert(b!=a)
    cmpd=d;
    int tot=0,mid=n>>1,u=0,v=mid;
    for(int i=0;i<n;i++){
        if(v>=n||(u<mid&&sleqt(a[u],a[v]))){
            if(flag||a[u].type==0) b[tot++]=a[u];
            u++;
        }
        else{
            if(flag||a[v].type==1) b[tot++]=a[v];
            v++;
        }
    }
    return tot;
}

//cdq分治三维
void cdq(int d,node*a,int n){//d=3
    static node buf[4][maxq<<3];
    node*b=buf[d];
    if(d==0){
        int sum=0;
        for(int i=0;i<n;i++){
            if(a[i].type==0) sum+=a[i].ct; // add
            else ans[a[i].id]+=sum; // query
        }
    }// n==0 n==1 return
    else if(n>=2){
        int mid=n>>1;
        cdq(d,a,mid),cdq(d,a+mid,n-mid);
        int tot=merge(a,n,b,d,0);
        cdq(d-1,b,tot);
        merge(a,n,b,d,1);
        for(int i=0;i<n;i++) a[i]=b[i];
    }
}

int qy[maxq][7];

int main(){
    int times;
    scanf("%d",&times);
    while(times--){
        int q; scanf("%d",&q);
        int w[6];
        for(int i=0;i<q;i++){
            scanf("%d",qy[i]+0);
            if(qy[i][0]==1) scanf("%d%d%d",qy[i]+1,qy[i]+2,qy[i]+3);
            else scanf("%d%d%d%d%d%d",qy[i]+1,qy[i]+2,qy[i]+3,qy[i]+4,qy[i]+5,qy[i]+6);
        }
        int tot=0;
        for(int i=0;i<q;i++){
            if(qy[i][0]==1) a[tot]=node(qy[i][1],qy[i][2],qy[i][3],tot,1,0),tot++;
            else {
                qy[i][1]--,qy[i][2]--,qy[i][3]--;
                a[tot]=node{qy[i][1],qy[i][2],qy[i][3],tot,1,1},tot++;//0 -
                a[tot]=node{qy[i][1],qy[i][2],qy[i][6],tot,1,1},tot++;//1 +
                a[tot]=node{qy[i][1],qy[i][5],qy[i][3],tot,1,1},tot++;//2 +
                a[tot]=node{qy[i][1],qy[i][5],qy[i][6],tot,1,1},tot++;//3 -
                a[tot]=node{qy[i][4],qy[i][2],qy[i][3],tot,1,1},tot++;//4 +
                a[tot]=node{qy[i][4],qy[i][2],qy[i][6],tot,1,1},tot++;//5 -
                a[tot]=node{qy[i][4],qy[i][5],qy[i][3],tot,1,1},tot++;//6 -
                a[tot]=node{qy[i][4],qy[i][5],qy[i][6],tot,1,1},tot++;//7 +
            }
        }
        for(int i=0;i<tot;i++) ans[i]=0;

        cdq(3,a,tot);
        tot=0;
        for(int i=0;i<q;i++){
            if(qy[i][0]==1) tot++;
            else{
                int*old=ans+tot;
                printf("%d\n",old[1]+old[2]+old[4]+old[7]-old[0]-old[3]-old[5]-old[6]);
                tot+=8;
            }
        }
    }
}

四维偏序- cdq分治降低两维+排序一维+树状数组一维

#include<bits/stdc++.h>
using namespace std;

const int maxq=5e4+5;

struct node{
    int w[3],id,ct,type;
    node()= default;
    node(int x,int y,int z,int id,int ct,int type):id(id),ct(ct),type(type){
        w[0]=x;
        w[1]=y;
        w[2]=z;
    }
};

int cmpd;
bool sleqt(node a,node b){
    for(int i=3-cmpd;i<3;i++) {
        if(a.w[i]!=b.w[i]) return a.w[i]<b.w[i];
    }
    return a.type<=b.type;
}

node a[maxq<<3];
int ans[maxq<<3];

int merge(node*a,int n,node*b,int d,int flag){  //assert(b!=a)
    cmpd=d;
    int tot=0,mid=n>>1,u=0,v=mid;
    for(int i=0;i<n;i++){
        if(v>=n||(u<mid&&sleqt(a[u],a[v]))){
            if(flag||a[u].type==0) b[tot++]=a[u];
            u++;
        }
        else{
            if(flag||a[v].type==1) b[tot++]=a[v];
            v++;
        }
    }
    return tot;
}

const int B=maxq*6+2;
int BIT[B];
//cdq分治两维
void cdq(int d,node*a,int n){//d=3
    static node buf[4][maxq<<3];
    node*b=buf[d];
    if(d==1){
        for(int i=0;i<n;i++){
            if(a[i].type==0) for(int j=a[i].w[2];j<B;j+=j&-j)BIT[j]+=a[i].ct; // add
            else for(int j=a[i].w[2];j>0;j-=j&-j)ans[a[i].id]+=BIT[j]; // query
        }
        for(int i=0;i<n;i++){
            if(a[i].type==0) for(int j=a[i].w[2];j<B;j+=j&-j)BIT[j]-=a[i].ct; // add
        }
    }// n==0 n==1 return
    else if(n>=2){
        int mid=n>>1;
        cdq(d,a,mid),cdq(d,a+mid,n-mid);
        int tot=merge(a,n,b,d,0);
        cdq(d-1,b,tot);
        merge(a,n,b,d,1);
        for(int i=0;i<n;i++) a[i]=b[i];
    }
}

int qy[maxq][7];

int main(){
    int times;
    scanf("%d",&times);
    while(times--){
        vector<int>disc;
        int q; scanf("%d",&q);
        int w[6];
        for(int i=0;i<q;i++){
            scanf("%d",qy[i]+0);
            if(qy[i][0]==1) {
                scanf("%d%d%d",qy[i]+1,qy[i]+2,qy[i]+3);
                for(int j=1;j<=3;j++) disc.push_back(qy[i][j]);
            }
            else {
                scanf("%d%d%d%d%d%d",qy[i]+1,qy[i]+2,qy[i]+3,qy[i]+4,qy[i]+5,qy[i]+6);
                for(int j=1;j<=6;j++) disc.push_back(qy[i][j]);
            }
        }
        sort(disc.begin(),disc.end());
        disc.erase(unique(disc.begin(),disc.end()),disc.end());
        int tot=0;
        for(int i=0;i<q;i++){
            if(qy[i][0]==1) {
                for(int j=1;j<=3;j++) qy[i][j]=lower_bound(disc.begin(),disc.end(),qy[i][j])-disc.begin()+2;
                a[tot]=node(qy[i][1],qy[i][2],qy[i][3],tot,1,0),tot++;
            }
            else {
                for(int j=1;j<=6;j++) qy[i][j]=lower_bound(disc.begin(),disc.end(),qy[i][j])-disc.begin()+2;
                qy[i][1]--,qy[i][2]--,qy[i][3]--;
                a[tot]=node{qy[i][1],qy[i][2],qy[i][3],tot,1,1},tot++;//0 -
                a[tot]=node{qy[i][1],qy[i][2],qy[i][6],tot,1,1},tot++;//1 +
                a[tot]=node{qy[i][1],qy[i][5],qy[i][3],tot,1,1},tot++;//2 +
                a[tot]=node{qy[i][1],qy[i][5],qy[i][6],tot,1,1},tot++;//3 -
                a[tot]=node{qy[i][4],qy[i][2],qy[i][3],tot,1,1},tot++;//4 +
                a[tot]=node{qy[i][4],qy[i][2],qy[i][6],tot,1,1},tot++;//5 -
                a[tot]=node{qy[i][4],qy[i][5],qy[i][3],tot,1,1},tot++;//6 -
                a[tot]=node{qy[i][4],qy[i][5],qy[i][6],tot,1,1},tot++;//7 +
            }
        }
        for(int i=0;i<tot;i++) ans[i]=0;

        cdq(3,a,tot);
        tot=0;
        for(int i=0;i<q;i++){
            if(qy[i][0]==1) tot++;
            else{
                int*old=ans+tot;
                printf("%d\n",old[1]+old[2]+old[4]+old[7]-old[0]-old[3]-old[5]-old[6]);
                tot+=8;
            }
        }
    }
}

kd树

转移自老blog

kdTree

       kd树是平衡树的多维拓展,说白了就是多维平衡树,它 和普通的的区别就在于,它是按照深度决定以哪个维度 作为建树划分标准的。(优化算法另当别论)
         算法在注释里面已经很清楚了。
#define pow2(x) ((x)*(x))
struct kdtree{
    static const int maxn=1e5,maxkd=5;
    static int kd,idx;
    int sz[maxn<<2],spile[maxn<<2];
    struct node{
        int w[maxkd];
        bool operator<(const node&rhs)const{return w[idx]<rhs.w[idx];}
    }t[maxn<<2],in[maxn];
    priority_queue<pair<double,node> > que;

    int maxvar(int l,int r){
        int ret=0;
        double val=-1;
        for(int i=0;i<kd;i++){
            double average=0;
            for(int j=l;j<=r;j++)average+=in[j].w[i];
            average/=r-l+1;
            double variance=0;
            for(int j=l;j<=r;j++)variance+=pow2(average-in[j].w[i]);
            if(variance>val)ret=i,val=variance;
        }
        return ret;
    }

    void build(int u,int l,int r){//用前初始化kd
        if(l>r){sz[u]=0; return;}
        int mid=(l+r)>>1;
        spile[u]=idx=maxvar(l,r);
        sz[u]=r-l+1;
        nth_element(in+l,in+mid,in+r+1);
        t[u]=in[mid];
        build(u<<1|0,l,mid-1);
        build(u<<1|1,mid+1,r);
    }
    void query(int u,int m,node&q){//用前清空que
        if(sz[u]==0)return ;
        pair<double,node> tmp(0,t[u]);
        for(int i=0;i<kd;i++)tmp.first+=pow2(q.w[i]-t[u].w[i]);//dist
        int dim=spile[u];
        int closed=u<<1|(t[u].w[dim]<=q.w[dim]);//离q近的儿子
        query(closed,m,q);
        if(que.size()<m){//not full
            que.push(tmp);
            query(closed^1,m,q);//因为没满,所以要搜
        }
        else{// it is full
            if(tmp.first<que.top().first) que.pop(),que.push(tmp);//replace
            if(pow2(q.w[dim]-t[u].w[dim])<que.top().first)query(closed^1,m,q);//than maybe beter ,else it is imposable
        }
    }
}KDT;
int kdtree::idx=0,kdtree::kd=0;

lct

转移自老blog

link cut tree

link cut tree是什么

        link cut tree是一种维护动态森林的数据结构,但我们常常用它来维护 一颗动态的树,维护这些动态树上路径的信息。
        在本质上他属于一种树剖分,将树剖成链,但是由于树是动态的,所以无法使用轻重剖分 ,因为重儿子可能会变化为轻儿子,轻儿子也可能变成重儿子,这里采取虚实剖分,按照虚链实链剖成链。

树的虚实剖分

        一个节点最多有一个实儿子,当然他也可以不拥有实儿子,除实儿子以外,其他儿子 被称作轻儿子。于是我们就像轻重剖分一样,把这棵树给剖掉了。这样剖,特别武断,看似无法保证时间复杂度,然而并非如此。证明过程较难,此处不做 描述。下面三张图都是二叉堆的虚实剖分,黑色边为实链,绿色为虚链

如何让被虚实剖分树具有动态

        因为实儿子的选取限 制少,所以任何节点都有可能成为实儿子,当一棵树的形态发生了变化, 这并不影响我们的剖分,显然断边不会让我们的剖分出现任何问题,故而我们什么都不需要做,连边的话,无脑另此边为虚边即可。于是树的动态性不会影响 剖分

如何维护路径信息

        此问题较复杂,我们先考虑根到某个节点的路径,我们是这样做的,变换虚实边,使得 经过根的实链的两个端点分别是根和那个节点。可以证明,这样的剖分存在。变换虚实边之后,那条路径就被我们剖了出来,链是由数据结构来维护的, 于是我们就可以解决该路径问题了。
        对于一般路径而言,我们直接强制换根来解决

我们该选取何种数据结构

        先来总结一下操作
                操作1:把实边变为虚边,对应到链上 就是把一条链切断,在数据结构中体现为分裂
                操作2:把实边变为虚边,对应到链上 就是把两条链合并,在数据结构中体现为合并
                操作3:换根?这个较复杂,暂时不考虑
                操作4:路径询问,在数据结构中体现为 整个数据结构维护的序列的性质,也即全段区间询问。
                操作5:路径修改,在数据结构中体现为 整个数据结构维护的序列的性质,也即全段区间修改。
        splay可以胜任,它支持分裂合并以及区间询问、修改,如果我们把整条链放入splay,用其中序遍历结果来 维护链,每一个splay再记录一个父亲,也即是此splay维护的链的父亲是谁?定义:链的父亲为链上深度最小的节点的父亲。,可以 证明,这些信息合并起来,对应到树是唯一的,于是问题基本解决
        现在我们来考虑换根,换根是怎么一回事?我们来看上文的最后一张图,根为1,如果我们想把根换为44,怎嘛办呢? 如果你足够聪明,是可以想到做法的,实际上我们只需要将链1->2->5->11->22->44反转即可。证明过程很简单,此处不做证明。
        至此,lct流程基本阐述完成,后面讲解细节操作。

核心操作access

        函数access(x),被调用后,我们会让起点在根,终点在x的链剖出,此链为过根节点的实链。操作很简单,我们 先将x伸展到它所在的那个splay的根,然后将它和他的右子树断开,为什么呢?因为他的右子树绝不在我们要的实链上,然后将它合并到此链的父亲上,但是此链的父亲可能已经有了 实儿子,那我们就将它断开,然后在把x子树合并上去,虽然是这么说的但是我们实际代码确实先合并子树,然后断链,其实是一样的。然后对其父亲作此操作,就结束了

后面的其他没啥技术含量,也特别简单,就不讲了,代码如下

struct Link_Cut_Tree{
    static const int N=1e5+555;
    int top,c[N][2],fa[N],xr[N],sta[N],rev[N],val[N];
    void init(int n){
        memset(fa,0,n<<2);
        memset(rev,0,n<<2);
        memset(c,0,n<<3);
    }
    inline void pushup(int x){xr[x]=xr[c[x][0]]^xr[c[x][1]]^val[x];}
    inline void pushdown(int x){
        int l=c[x][0],r=c[x][1];
        if(rev[x]){
            rev[l]^=1;rev[r]^=1;rev[x]^=1;
            swap(c[x][0],c[x][1]);
        }
    }
    inline bool isroot(int x){return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;}
    void rotate(int x){
        int y=fa[x],z=fa[y],xis=c[y][1]==x,yis=c[z][1]==y;//
        if(!isroot(y))c[z][yis]=x;//son
        fa[x]=z;fa[y]=x;fa[c[x][xis^1]]=y;//father
        c[y][xis]=c[x][xis^1];c[x][xis^1]=y;//son
        pushup(y);pushup(x);
    }
    void splay(int x){
        top=1;sta[top]=x;//init stack
        for(int i=x;!isroot(i);i=fa[i])sta[++top]=fa[i];//update stack
        for(int i=top;i;i--)pushdown(sta[i]);//pushroad
        while(!isroot(x)){
            int y=fa[x],z=fa[y];
            if(!isroot(y)){
                if((c[y][0]==x)^(c[z][0]==y))rotate(x);
                else rotate(y);
            }rotate(x);
        }
    }
    void access(int x){for(int t=0;x;t=x,x=fa[x])splay(x),c[x][1]=t,pushup(x);}
    int treeroot(int x){access(x);splay(x);while(c[x][0])x=c[x][0];return x;}
    void makeroot(int x){access(x);splay(x);rev[x]^=1;}
    void cut(int x){access(x);splay(x);fa[c[x][0]]=fa[x];c[x][0]=0;fa[x]=0;}//cut the subtree x and let x to be the root
    void link(int x,int y){makeroot(x);fa[x]=y;}
}tr;

map

转移自老blog

map

struct mymap {
    static const int N=(1<<22);
    int key[N], val[N];

    int query(int x) {
        int i = x & (N - 1);
        while (key[i] != -1 && key[i] != x) i = (i + 1) & (N - 1);
        return val[i];
    }

    void update(int x, int id) {// can't find then return -1
        int i = x & (N - 1);
        while (key[i] != -1 && key[i] != x) i = (i + 1) & (N - 1);
        key[i] = x, val[i] = id;
    }

    void clear() {
        memset(key, -1, sizeof(key));
        memset(val, -1, sizeof(val));
    }
};

rmq

转移自老blog

rmq倍增

struct RMQ{
    static const int maxn=1000,lgmaxn=12;
    static int lg[maxn];
    int mx[maxn][lgmaxn];

    RMQ(){//构造函数
        if(lg[2]!=1){
            for(int i=2;i<maxn;i++){ //因为lg(1)=0
                lg[i]=(i&-i)==i?lg[i-1]+1:lg[i-1];
            }
        }
    }

    void build(int n,int *a){
        for(int i=0;i<=n;i++)mx[i][0]=a[i];
        for(int j=1;j<=lg[n+1];j++)
            for(int i=0;i+(1<<j)-1<=n;i++)
                mx[i][j]=max(mx[i][j-1],mx[i+(1<<(j-1))][j-1]);
    }

    int query(int l,int r){
        int k=lg[r-l+1];
        return max(mx[l][k],mx[r-(1<<k)+1][k]);
    }
};
inline int max(int a,int b,int c,int d){return max(max(a,b),max(c,d));}
struct RMQ2{//写的时候下标从0开始,可以当作下标为1开始来使用
    static const int maxn=310,lgmaxn=9;//确保 ( 1<<(lgmaxn-1) ) > maxn
    static int lg[maxn];//log2(x)向下取整
    int mx[maxn][maxn][lgmaxn][lgmaxn];
    RMQ2(){//构造函数不用管的
        if(lg[2]!=1){
            for(int i=2;i<maxn;i++){ //因为lg(1)=0
                lg[i]=(i&-i)==i?lg[i-1]+1:lg[i-1];
            }
        }
    }
    void build(int row,int col,int a[][maxn]){//[0,row],[0,col]
        for(int i=0;i<=row;i++)for(int j=0;j<=col;j++)mx[i][j][0][0]=a[i][j];

        for(int ii=0; (1<<ii)<=row+1; ii++){
            for(int jj=0; (1<<jj)<=col+1; jj++){
                for(int i=0; i+(1<<ii)-1<=row; i++){
                    for(int j=0; j+(1<<jj)-1<=col; j++){
                        if     (ii!=0)mx[i][j][ii][jj]=max(mx[i][j][ii-1][jj],mx[i+(1<<(ii-1))][j][ii-1][jj]);
                        else if(jj!=0)mx[i][j][ii][jj]=max(mx[i][j][ii][jj-1],mx[i][j+(1<<(jj-1))][ii][jj-1]);
                    }
                }
            }
        }
    }
    int query(int r1,int c1,int r2,int c2){
        if(r1>r2)swap(r1,r2);
        if(c1>c2)swap(c1,c2);
        int kr=lg[r2-r1+1], r3=r2+1-(1<<kr);
        int kc=lg[c2-c1+1], c3=c2+1-(1<<kc);
        return max(mx[r1][c1][kr][kc],mx[r1][c3][kr][kc],
                   mx[r3][c3][kr][kc],mx[r3][c1][kr][kc]);
    }
};int RMQ2::lg[maxn];
/******************    on预处理 O1在线查询rmq   *****************/
double Ospace(double n,double blk){
    return pow(2,blk)+2*n/blk+blk+5*n+2*n/blk*ceil(log(2*n/blk)/log(2))+2*n/blk;
}
const int maxn=2e7+21,blk=20,lgmxn=22;//  2^b+2n/b+b    +   2n+2n+n+2n/b*lg+2n/b) = 2^b+5n+b+2n/b(2+lg)
int pre[1<<blk],lg[(maxn<<1)/blk],hi[blk];
void prework(){
    hi[0]=0; for(int i=1;i<blk;i++) hi[i]=hi[i-1]>>1|(1<<(blk-1));
    lg[1]=0; for(int i=2;i*blk<(maxn<<1);i++) lg[i]=lg[i>>1]+1;
    vector<int>sum(1<<blk);// 节约空间
    int b=(1<<blk)-1;
    for(int i=0;i<(1<<blk);i++){
        sum[i^b]=i&1?-1:1;pre[i^b]=0;
        if(sum[i>>1^b]<0) sum[i^b]+=sum[i>>1^b],pre[i^b]=pre[i>>1^b]+1;
    }
}
struct o1rmq{
    int dep[maxn<<1],p[maxn<<1],fst[maxn];
    int rmq[(maxn<<1)/blk][lgmxn],bin[(maxn<<1)/blk];
    int *ls=rmq[0],*rs=ls+maxn;// 内存复用

    void upd(int&x,int y){if(dep[x]>dep[y]) x=y;}
    void build(int n,int*dat){// [0,n)
        int *s=fst,top=0,step=-1;// 内存复用,  使用单调栈为dat建立笛卡尔树
        for(int i=0;i<n;i++){
            ls[i]=rs[i]=-1;
            while(top>0&&dat[i]<dat[s[top]]) ls[i]=s[top--];  // < is min and > is max
            if(top>0)rs[s[top]]=i;
            s[++top]=i;
        }
        dfs(s[1],1,step);//对笛卡尔树进行ett分解,此后ls,rs,栈s将无用处,可以丢弃。

        int exn=(2*n-1+blk-1)/blk;//对ett分块  拓展到blk的倍数
        for(int i=2*n-1;i<exn*blk;i++)dep[i]=dep[i-1]+1;
        for(int i=0;i<exn;i++){
            bin[i]=0;
            for(int j=0;j<blk;j++) if(j==0||dep[i*blk+j]>dep[i*blk+j-1]) bin[i]|=1<<j;
            rmq[i][0]=i*blk+pre[bin[i]];
        }
        for(int j=1;0+(1<<j)<=exn;j++){// 对块进行rmq
            for(int i=0;i+(1<<j)<=exn;i++){//[i,i+1<<j)
                rmq[i][j]=rmq[i][j-1]; upd(rmq[i][j],rmq[i+(1<<(j-1))][j-1]);
            }
        }
    }
    void dfs(int u,int d,int&step){
        p[++step]=u;
        dep[step]=d;
        fst[u]=step;
        if(ls[u]!=-1){
            dfs(ls[u],d+1,step);
            p[++step]=u;
            dep[step]=d;
        }
        if(rs[u]!=-1){
            dfs(rs[u],d+1,step);
            p[++step]=u;
            dep[step]=d;
        }
    }
    int query(int l,int r){//[l,r]  query max value, return idx of min value
        l=fst[l],r=fst[r]; if(l>r)swap(l,r);
        int l1=l/blk,l2=l%blk,r1=r/blk,r2=r%blk;
        if(l1==r1) return p[l+pre[bin[l1]>>l2|hi[blk-(r2-l2+1)]]];
        else{
            int bl=l1*blk==l?l1:l1+1,br=r1*blk+blk-1==r?r1:r1-1;
            int ret=l;
            if(bl<=br) {
                int k=lg[br-bl+1];
                upd(ret,rmq[bl][k]);
                upd(ret,rmq[br-(1<<k)+1][k]);
            }
            if(l1!=bl) upd(ret,l+pre[bin[l1]>>l2|hi[blk-(blk-1-l2+1)]]);
            if(r1!=br) upd(ret,((br+1)*blk)+pre[bin[r1]|hi[blk-(r2-0+1)]]);
            return p[ret];
        }
    }
}rmq;
分块rmq
/******************  O(7n)-O(3) rmq  *****************/
int ospace(int n,int base){return (n>>base)+(n>>base)*ceil(log(n>>base)/log(2))+n*(base+1);}
const int base=5,blk=1<<base,maxn=2e7+7+blk,lgnb=21;
int lg[maxn>>base];
void prework(){
    lg[1]=0; for(int i=2;i*blk<maxn;i++) lg[i]=lg[i>>1]+1;
}
struct normalrmq{
    int rmq[blk][base+1];
    void build(int n,int*a){//[0,n)
        for(int i=0;i<n;i++)rmq[i][0]=a[i];
        for(int j=1;0+(1<<j)-1<n;j++)
            for(int i=0;i+(1<<j)-1<n;i++)
                rmq[i][j]=max(rmq[i][j-1],rmq[i+(1<<(j-1))][j-1]);
    }
    int query(int l,int r){
        int k=lg[r-l+1];
        return max(rmq[l][k],rmq[r-(1<<k)+1][k]);
    }
};
struct O7nO3rmq{
    int rmq[maxn/blk][lgnb];
    normalrmq rmq2[maxn/blk];
    void build(int n,int*a){//
        int exn=(n+blk-1)/blk;
        for(int i=0;i<exn;i++){
            rmq2[i].build(blk,a+i*blk);
            rmq[i][0]=rmq2[i].query(0,blk-1);
        }
        for(int j=1;0+(1<<j)-1<exn;j++)
            for(int i=0;i+(1<<j)-1<exn;i++)
                rmq[i][j]=max(rmq[i][j-1],rmq[i+(1<<(j-1))][j-1]);
    }
    int query(int l,int r){
        int l1=l/blk,l2=l%blk,r1=r/blk,r2=r%blk;
        if(l1==r1) return rmq2[l1].query(l2,r2);
        else {
            int bl=l1*blk==l?l1:l1+1,br=r1*blk+blk-1==r?r1:r1-1;
            int ret=1<<31;
            if(bl<=br) {
                int k=lg[br-bl+1];
                ret=max(rmq[bl][k],rmq[br-(1<<k)+1][k]);
            }
            if(l1!=bl) ret=max(ret,rmq2[l1].query(l2,blk-1));
            if(r1!=br) ret=max(ret,rmq2[r1].query(0,r2));
            return ret;
        }
    }
}rmq;

splay

转移自老blog

splay

struct splay_tree{
    static const int maxn=2e6+10;
    int ch[maxn][2],fa[maxn];
    int miv[maxn],val[maxn],add[maxn],rev[maxn],siz[maxn];
    int rub[maxn],rub_;//回收池
    int rt,tot;
    //数据结束

    void pushup(int h){//维持,回溯
        int l=ch[h][0],r=ch[h][1];
        miv[h]=val[h];
        if(l)miv[h]=min(miv[h],miv[l]);
        if(r)miv[h]=min(miv[h],miv[r]);
        siz[h]=siz[l]+siz[r]+1;
    }

    void pushdown(int h){//下放懒惰标记
        int l=ch[h][0],r=ch[h][1];
        if(add[h]){
            add[l]+=add[h];val[l]+=add[h];miv[l]+=add[h];
            add[r]+=add[h];val[r]+=add[h];miv[r]+=add[h];
            add[h]=0;
        }
        if(rev[h]){
            rev[l]^=1;rev[r]^=1;rev[h]^=1;
            swap(ch[h][0],ch[h][1]);
        }
    }

    void rotate(int h){//旋转
        int f=fa[h],g=fa[f];
        if(g){
            int c=(f==ch[g][1]);
            ch[g][c]=h;
        }
        fa[f]=h;fa[h]=g;
        int c=(h==ch[f][1]);
        fa[ch[h][!c]]=f;
        ch[f][c]=ch[h][!c];
        ch[h][!c]=f;
        if(f==rt) rt=h;
        pushup(f);
        pushup(h);
    }

    void splay(int h,int aim){//伸展
        static int sta[maxn];
        int top=1;sta[top]=h;
        for(int i=h;fa[i]!=0;i=fa[i])sta[++top]=fa[i];
        for(int i=top;i>=1;--i)pushdown(sta[i]);
        while(fa[h]^aim){
            int f=fa[h],g=fa[f];
            if(g^aim)rotate(f);
            rotate(h);
        }
        pushup(h);
    }

    void newnode(int&h,int f,int val_){
        if(rub_!=-1)h=rub[rub_--];//如果rub是空的,我们就从rub里面找
        else h=++tot;//否则使用新的

        ch[h][0]=ch[h][1]=0;
        fa[h]=f;

        miv[h]=val[h]=val_;
        add[h]=rev[h]=0;
        siz[h]=1;
    }

    void built(int&x,char*data,int l,int r,int f){
        if(l>r)return;
        int mid=(l+r)>>1;
        newnode(x,f,data[mid]);
        built(ch[x][0],data,l,mid-1,x);
        built(ch[x][1],data,mid+1,r,x);
        pushup(x);
    }

    void ini(){//建只含有两个节点的空树
        rub_=-1;tot=0;
        newnode(rt,0,0);
        newnode(ch[rt][1],rt,0);
        pushup(ch[rt][1]);
        pushup(rt);
    }

    void insert(int a,char*data,int l,int r){//把data添加在a的后面,
        int x=id(a),y=id(a+1);
        splay(x,0); splay(y,x);
        built(ch[y][0],data,l,r,y);
        pushup(y);
        pushup(x);
    }

    int id(int k){//返回第k个数,下标是从1开始的
        int h;
        pushdown(rt);
        for(h=rt;siz[ch[h][0]]+1!=k;){
            if(siz[ch[h][0]]+1>k)h=ch[h][0];
            else{
                k-=(siz[ch[h][0]]+1);
                h=ch[h][1];
            }
            pushdown(h);
        }
        return h;
    }
    //以上函数必备,尽量不要修改
    ////////////////////////

    void rubbish(int&rt){
        if(ch[rt][0])rubbish(ch[rt][0]);
        if(ch[rt][1])rubbish(ch[rt][1]);
        rub[++rub_]=rt;
        rt=0;
    }

    void erase(int a,int b){
        int x=id(a-1), y=id(b+1);
        splay(x,0); splay(y,x);
        rubbish(ch[y][0]);
        pushup(y); pushup(x);
    }

    int getmin(int a,int b){
        int x=id(a-1), y=id(b+1);
        splay(x,0); splay(y,x);
        return miv[ch[y][0]];
    }

    void addval(int a,int b,int d){
        int x=id(a-1), y=id(b+1);
        splay(x,0); splay(y,x);
        int w=ch[y][0];
        add[w]+=d;
        val[w]+=d;
        miv[w]+=d;
        pushup(y); pushup(x);
    }

    void reserve(int a,int b){
        int x=id(a-1), y=id(b+1);
        splay(x,0); splay(y,x);
        rev[ch[y][0]]^=1;
    }

}tree;

主席树

转移自老blog

主席树

------ 2019.4.30

什么是主席树

        主席树是可持久化权值线段树,支持点修改,区间查询。

静态数组区间第k大

        先来个题,给一个数组,多组询问,每次询问区间第k大(小),

为数组的每一个前缀建立一颗线段树

        但是这样子,空间会爆炸,时间也一样爆炸,但是我们发现前缀 之间是有联系的,两个相邻的前缀之间只有一个权值的差距,所以可以考虑利用以前的节点信息以节省空间和时间。

具体实现

        因为两个相邻的前缀之间只有一个权值的差距,所以两颗线段树树之间 只有一条链的差距。我们尝试让新树的指针指向旧树,然后对新树那条有区别的链重新开辟空间即可。

时空复杂度分析

        因为一条链的差距,导致每次建树,空间至多开辟lgn,同理,时间为lgn

如何解决静态区间第k大

        我们有了每一个前缀的权值线段树,于是我们让两个前缀权值线段树相减,即可得到 任意区间的权值线段树,现在问题化简了,已知某个区间的权值线段树,询问区间第k大,我们在这颗权值线段树上二分答案即可。二分是这样实现的, 如果当前跑到了叶子结点,说明叶子节点权值为答案,否则把k和当前节点左子树的权值和比较,若k小,说明答案在右子树,否则在左子树。

一个优化(可以先不看)

        很多人写主席树,带一个build函数,用来建立一颗第一个版本的树,理解确实好理解, 然而,真的有必要吗?其实是没有的,我们来看看那颗树长啥样?由于没有权值,所以那颗树维护的所有节点的权值都是0,注意:都是0。也就是说 你浪费了2*n的空间,存了一堆0?是的,这里我们来优化一下,我们来看这样一个节点,他的权值为0,他的左儿子指向自己,他的右儿子指向自己, 看看,这是一个包含了一个节点的非简单图,他有自环来着。我们尝试对这一个节点安装访问曾经访问过的节点的方式带着区间端点dfs下去,你会发现一个奇妙的 事情,你得到了和之前花费2*n个节点建立空树一样的效果。于是我们优化了这一个空间复杂度,此优化在此不太明显,O(lg)-O(1)算啥呀。但是 当我们处理后面的动态区间问题时,此优化起到了非常大的作用,这就是图的优势打个广告:此优化像极了后缀自动机


动态数组区间第k大

        前面的题目已经解决了,但是当数组变化(点修改)伴随着区间第k大查询的时候,我们该怎么办呢?

重新研究主席树

        主席树,说白了,就是原数组所有前缀的权值线段树+时空优化。查询的时候同个两个 前缀和的差值得到任意区间的权值线段树。我们发现这和一个题目很相似。

区间和查询

        想必不少人都做过这样的一个题目,给你一个固定的数组,给出很多询问,每次询问 一个区间,让你输出区间和。这个题目很简单,我们只需要预处理前缀和,对于任意询问,用两个前缀和向减即可解决,

带修改的区间和查询

        当上一个题目发生变化:数组可能会点修改,点修改伴随着区间和查询,的时候,我们 就不能用前缀和了,这样很困难,于是树状数组出现了。限于重点,此处不解释树状数组

将树状数组的思路用到主席树上(带修主席树)

        我们让主席树换一种建法,不用前缀和,用树状数组的方案,每一颗主席树rt[i]维护 区间(i&(i-1),i]的权值线段树。如此一来,当我们进行点修改的时候,只需要修改lg颗权值线段树,当我们查询的时候,要lg颗线段树才能组成 任意区间的权值线段树。相当于牺牲了一部分查询时间,换取更新操作的加速。

带修主席树?线段树动态开点?

        当主席树方式变了之后,我们惊讶的发现,权值线段树不能简单的继承他的儿子了,他比儿子 多好多新的权值信息要维护,比方说1100(12),他的儿子是1000(8),他俩差了100(4)个权值,如果我们还是采用之前的继承儿子权值线段树的方式,编码会很复杂 这里提出一种新的方法:超级超级超级大大大暴力建树,每一个树状数组节点(权值线段树)我们都从0开始建树。有兴趣可以去打表试试,看似暴力其实一点都不暴力, 除非你不用上文谈到的的那个"一个优化" 我们最多调用update函数lg*lg次。于是,这个带修主席树,被yyb成为了线段树动态开点。 不知道yyb,你可太骚了,自己百度去

线段树套主席树

        此坑待填。


树形主席树

        主席树太强了,我们骚里个骚,把它放到树上去,来个树套树

静态树上路径第k大

        是这样一个问题,给你一棵树,每个节点都有权,很多询问,问你任意两点之间的最短路径上经过的点中 点权第k大是谁

树上差分

        前缀也没谁了,树上一个点前缀就是这个点到根的路径上经过的点的集合,我们安装这种想法建立一个树上的 主席树,儿子是父亲的继承版本中的一个,父亲是儿子的前一个版本,时空复杂度不高,

树上任意路径?

        比方说u->v ,我们假设lca为u和v的lca,假设fa为lca的父亲,那么我们用u的权值线段树加上v的权值线段树减去lca的 权值线段树再减去fa的权值线段树,得到的就是u->v这条路径的权值线段树,ok你已经学完了此博文

可变的树上路径第k大

https://www.luogu.org/problemnew/solution/P4175
// 没有build 因为不需要 空树就是rt[0],是建好了的
const int maxn = 2e5+5;
int ls[maxn*20*2],rs[maxn*20*2],siz[maxn*20*2],tot,rt[maxn];//update用了几次,就要乘以多少

void ini(){tot=0;}

void update(int pre,int&u,int l,int r,int pos,int val){//把u按照pre复制,然后更新pos
    u=++tot;
    ls[u]=ls[pre];rs[u]=rs[pre];
    siz[u]=siz[pre]+val;
    if(l==r)return ;
    int mid=(l+r)>>1;
    if(pos<=mid) update(ls[pre],ls[u],l,mid,pos,val);
    else update(rs[pre],rs[u],mid+1,r,pos,val);
}

int query(int x,int y,int l,int r,int k){//查询(y树-x树)差值的区间第k小
    if(l==r)return l;
    int s=siz[ls[y]]-siz[ls[x]];
    int mid=(l+r)>>1;
    if(k<=s) return query(ls[x],ls[y],l,mid,k);
    else return query(rs[x],rs[y],mid+1,r,k-s);
}

int query(int x,int l,int r,int k){//查询树上有多少个数大于等于k,用于询问区间不同数的个数
    if(l==r) return k<=l?siz[x]:0;
    int mid=(l+r)>>1;
    if(k<=mid) return siz[rs[x]]+query(ls[x],l,mid,k);
    else return query(rs[x],mid+1,r,k);
}


//////////////////////////////////////////////////////////////////////////////////////////



//树状数组套主席树
int ls[maxn*20*20],rs[maxn*20*20],siz[maxn*20*20];
int rt[maxn],Tl[maxn],Tr[maxn],Tl_,Tr_,tot;

// n-> 树状数组节点个数     maxval->主席树叶子结点
// for(int j=x;j<=n;j+=j&-j) update(rt[j],1,maxval,pos,+1/-1); //注意pos写不要大常数
void update(int&u,int l,int r,int pos,int val){
    if(u==0) u=++tot;//,ls[u]=rs[u]=siz[u]=0;
    siz[u]+=val;
    if(l==r) return;
    int mid=(l+r)>>1;
    if(pos<=mid) update(ls[u],l,mid,pos,val);
    else update(rs[u],mid+1,r,pos,val);
}

//query(x-1,y,1,maxval,z)
int query(int x,int y,int l,int r,int k){
    Tl_=Tr_=0;
    for(int i=x;i;i&=i-1)Tl[Tl_++]=rt[i];
    for(int i=y;i;i&=i-1)Tr[Tr_++]=rt[i];
    while(l<r){
        int sum=0, mid=(l+r)>>1;
        for(int i=0;i<Tl_;i++) sum-=siz[ls[Tl[i]]];
        for(int i=0;i<Tr_;i++) sum+=siz[ls[Tr[i]]];
        if(k<=sum) {
            for(int i=0;i<Tl_;i++) Tl[i]=ls[Tl[i]];
            for(int i=0;i<Tr_;i++) Tr[i]=ls[Tr[i]];
            r=mid;
        }
        else{
            for(int i=0;i<Tl_;i++) Tl[i]=rs[Tl[i]];
            for(int i=0;i<Tr_;i++) Tr[i]=rs[Tr[i]];
            l=mid+1;
            k-=sum;
        }
    }
    return l;
}
//////////////////////////////////////////////////////////////////////////////////////////



int ls[maxn*20],rs[maxn*20],siz[maxn*20];
int tot,rt[maxn];
struct star{int v,nex;}g[maxn<<1];
int head[maxn],g_;
int w[maxn],fa[maxn][20],dep[maxn];

//tree
void ini(int n){
    g_=0;
    memset(head,-1,(n+1)*sizeof(head[0]));
}
void add_edge(int u,int v){
    g[g_].v=v;
    g[g_].nex=head[u];
    head[u]=g_++;

    g[g_].v=u;
    g[g_].nex=head[v];
    head[v]=g_++;
}

//lca
void build_lca(int cur=1,int father=0){
    dep[cur]=dep[father]+1;
    fa[cur][0]=father;
    for (int i=1;i<20;i++) fa[cur][i]=fa[fa[cur][i-1]][i-1];
    for(int i=head[cur];~i;i=g[i].nex){
        if(g[i].v==father)continue;
        build_lca(g[i].v,cur);
    }
}
int lca(int u,int v){
    if(dep[u]>dep[v])swap(u,v);//so dep[u]<dep[v]
    for(int i=19;i>=0;i--){
        if(dep[v]-(1<<i)>=dep[u])v=fa[v][i];
    }
    if(u==v)return u;
    for(int i=19;i>=0;i--){
        if(fa[u][i]!=fa[v][i]){
            u=fa[u][i];
            v=fa[v][i];
        }
    }
    return fa[u][0];
}

//seg tree
void update(int pre,int&u,int l,int r,int pos){
    u=++tot;
    ls[u]=ls[pre],rs[u]=rs[pre];
    siz[u]=siz[pre]+1;
    if(l==r)return ;
    int mid=(l+r)>>1;
    if(pos<=mid) update(ls[pre],ls[u],l,mid,pos);
    else update(rs[pre],rs[u],mid+1,r,pos);
}
void build_seg(int cur=1,int fa=0){//root
    update(rt[fa],rt[cur],1,disc_,w[cur]);
    for(int i=head[cur];~i;i=g[i].nex){
        if(g[i].v==fa)continue;
        build_seg(g[i].v,cur);
    }
}
// query(rt[u],rt[v],rt[lca],rt[fa[lca][0]],1,maxval,kth);
int query(int u,int v,int lca,int fa,int l,int r,int k){
    if(l==r)return l;
    int s=siz[ls[u]]+siz[ls[v]]-siz[ls[lca]]-siz[ls[fa]];
    int mid=(l+r)>>1;
    if(k<=s) return query(ls[u],ls[v],ls[lca],ls[fa],l,mid,k);
    else     return query(rs[u],rs[v],rs[lca],rs[fa],mid+1,r,k-s);
}
//////////////////////////////////////////////////////////////////////////////////////////

区间加+区间乘+区间求和的双标记线段树

转移自老blog

线段树

typedef long long ll;
#define ls (rt<<1)
#define rs (ls|1)
#define ml ((l+r)>>1)
#define mr (ml+1)
const ll maxn=1e5+55;
ll mod=123456789;
ll mul[maxn<<2],add[maxn<<2],sum[maxn<<2],a[maxn];

void push_down(ll rt,ll l,ll r){
    // if(l!=r) push_down(ls,l,ml),push_down(rs,mr,r);
    if(mul[rt]!=1){
        mul[ls]=mul[ls]*mul[rt]%mod;
        add[ls]=add[ls]*mul[rt]%mod;
        sum[ls]=sum[ls]*mul[rt]%mod;

        mul[rs]=mul[rs]*mul[rt]%mod;
        add[rs]=add[rs]*mul[rt]%mod;
        sum[rs]=sum[rs]*mul[rt]%mod;

        mul[rt]=1;
    }
    if(add[rt]!=0){
        add[ls]=(add[ls]+add[rt])%mod;
        sum[ls]=(sum[ls]+add[rt]*(ml-l+1))%mod;

        add[rs]=(add[rs]+add[rt])%mod;
        sum[rs]=(sum[rs]+add[rt]*(r-mr+1))%mod;

        add[rt]=0;
    }
}

void push_up(ll rt,ll l,ll r){
    sum[rt]=(sum[ls]+sum[rs])%mod;
}

void build(ll rt,ll l,ll r){
    mul[rt]=1;
    add[rt]=0;
    if(l==r){
        sum[rt]=a[l];
    }
    else{
        build(ls,l,ml);
        build(rs,mr,r);
        push_up(rt,l,r);
    }
}

void update_add(ll rt,ll l,ll r,ll ql,ll qr,ll d){
    if(l!=r)push_down(rt,l,r);
    if(ql<=l&&r<=qr){
        sum[rt]=(sum[rt]+(r-l+1)*d)%mod;
        add[rt]=d;
    }
    else{
        if(ml>=ql) update_add(ls,l,ml,ql,qr,d);
        if(mr<=qr) update_add(rs,mr,r,ql,qr,d);
        push_up(rt,l,r);
    }
}

void update_mul(ll rt,ll l,ll r,ll ql,ll qr,ll d){
    if(l!=r)push_down(rt,l,r);
    if(ql<=l&&r<=qr){
        sum[rt]=sum[rt]*d%mod;
        mul[rt]=d;
    }
    else{
        if(ml>=ql) update_mul(ls,l,ml,ql,qr,d);
        if(mr<=qr) update_mul(rs,mr,r,ql,qr,d);
        push_up(rt,l,r);
    }
}

ll query_sum(ll rt,ll l,ll r,ll ql,ll qr){
    if(l!=r)push_down(rt,l,r);
    if(ql<=l&&r<=qr){
        return sum[rt];
    }
    else{
        ll ret=0;
        if(ml>=ql) ret+=query_sum(ls,l,ml,ql,qr);
        if(mr<=qr) ret+=query_sum(rs,mr,r,ql,qr);
        return ret%mod;
    }
}

区间加+区间乘+区间赋值+区间p次方求和的三标记线段树

转移自老blog

线段树

//hdu 4578
#define ls (rt<<1)
#define rs (ls|1)
#define ml ((l+r)>>1)
#define mr (ml+1)
const int maxn=1e5+55;
int mod=10007;
int mul[maxn<<2],add[maxn<<2],cov[maxn<<2],sum[3][maxn<<2],a[maxn];

inline int modmul(int a,int b){return a*b%mod;}
inline int modmul(int a,int b,int c){return modmul(modmul(a,b),c);}
inline int modmul(int a,int b,int c,int d){return modmul(modmul(a,b,c),d);}

void push_son(int son,int l,int r,int covrt,int mulrt,int addrt){
    if(covrt!=-1){
        sum[0][son]=modmul(covrt,r-l+1);
        sum[1][son]=modmul(covrt,sum[0][son]);
        sum[2][son]=modmul(covrt,sum[1][son]);
        cov[son]=covrt;
        mul[son]=1;
        add[son]=0;
    }
    if(mulrt!=1){
        sum[0][son]=modmul(sum[0][son],mulrt);
        sum[1][son]=modmul(sum[1][son],mulrt,mulrt);
        sum[2][son]=modmul(sum[2][son],mulrt,mulrt,mulrt);
        mul[son]=modmul(mul[son],mulrt);
        add[son]=modmul(add[son],mulrt);
    }
    if(addrt!=0){//(x+d)^3=x^3+3*x^2*d+3*x*d^2+d^3
        sum[2][son]=(sum[2][son]+modmul(3,sum[1][son],addrt)+modmul(3,sum[0][son],addrt,addrt)+modmul(r-l+1,addrt,addrt,addrt))%mod;
        sum[1][son]=(sum[1][son]+modmul(2,addrt,sum[0][son])+modmul(r-l+1,addrt,addrt))%mod;
        sum[0][son]=(sum[0][son]+modmul(r-l+1,addrt))%mod;
        add[son]=(add[son]+addrt)%mod;
    }
}

void push_down(int rt,int l,int r){
    push_son(ls,l,ml,cov[rt],mul[rt],add[rt]);
    push_son(rs,mr,r,cov[rt],mul[rt],add[rt]);
    cov[rt]=-1; mul[rt]=1; add[rt]=0;
}

void push_up(int rt,int l,int r){
    sum[0][rt]=(sum[0][ls]+sum[0][rs])%mod;
    sum[1][rt]=(sum[1][ls]+sum[1][rs])%mod;
    sum[2][rt]=(sum[2][ls]+sum[2][rs])%mod;
}

void build(int rt,int l,int r){
    cov[rt]=-1;
    mul[rt]=1;
    add[rt]=0;
    if(l==r){
        sum[0][rt]=a[l];
        sum[1][rt]=modmul(a[l],a[l]);
        sum[2][rt]=modmul(a[l],a[l],a[l]);
    }
    else{
        build(ls,l,ml);
        build(rs,mr,r);
        push_up(rt,l,r);
    }
}

void update(int rt,int l,int r,int ql,int qr,int d,int type){
    if(l!=r) push_down(rt,l,r);
    if(ql<=l&&r<=qr){
        if(type==1) push_son(rt,l,r,-1,1,d);
        if(type==2) push_son(rt,l,r,-1,d,0);
        if(type==3) push_son(rt,l,r, d,1,0);
    }
    else{
        if(ml>=ql) update(ls,l,ml,ql,qr,d,type);
        if(mr<=qr) update(rs,mr,r,ql,qr,d,type);
        push_up(rt,l,r);
    }
}

int query(int rt,int l,int r,int ql,int qr,int pw){// pw-1
    if(l!=r) push_down(rt,l,r);
    if(ql<=l&&r<=qr){
        return sum[pw][rt];
    }
    else{
        int ret=0;
        if(ml>=ql) ret+=query(ls,l,ml,ql,qr,pw);
        if(mr<=qr) ret+=query(rs,mr,r,ql,qr,pw);
        return ret%mod;
    }
}

更新了动态开点的线段树

//更新动态开点
#define ml ((l+r)>>1)
#define mr (ml+1)
const int maxn=1e5+55;
int mod=10007;
int mul[maxn*2],add[maxn*2],cov[maxn*2],sum[3][maxn*2],ls[maxn*2],rs[maxn*2],a[maxn],tot;

inline int modmul(int a,int b){return a*b%mod;}
inline int modmul(int a,int b,int c){return modmul(modmul(a,b),c);}
inline int modmul(int a,int b,int c,int d){return modmul(modmul(a,b,c),d);}

void push_son(int&son,int l,int r,int covrt,int mulrt,int addrt){
    if(son==0) {
        son=++tot;
        mul[son]=1;
        add[son]=0;
        cov[son]=-1;
        sum[0][son]=0;
        sum[1][son]=0;
        sum[2][son]=0;
        ls[son]=0;
        rs[son]=0;
    }
    if(covrt!=-1){
        sum[0][son]=modmul(covrt,r-l+1);
        sum[1][son]=modmul(covrt,sum[0][son]);
        sum[2][son]=modmul(covrt,sum[1][son]);
        cov[son]=covrt;
        mul[son]=1;
        add[son]=0;
    }
    if(mulrt!=1){
        sum[0][son]=modmul(sum[0][son],mulrt);
        sum[1][son]=modmul(sum[1][son],mulrt,mulrt);
        sum[2][son]=modmul(sum[2][son],mulrt,mulrt,mulrt);
        mul[son]=modmul(mul[son],mulrt);
        add[son]=modmul(add[son],mulrt);
    }
    if(addrt!=0){//(x+d)^3=x^3+3*x^2*d+3*x*d^2+d^3
        sum[2][son]=(sum[2][son]+modmul(3,sum[1][son],addrt)+modmul(3,sum[0][son],addrt,addrt)+modmul(r-l+1,addrt,addrt,addrt))%mod;
        sum[1][son]=(sum[1][son]+modmul(2,addrt,sum[0][son])+modmul(r-l+1,addrt,addrt))%mod;
        sum[0][son]=(sum[0][son]+modmul(r-l+1,addrt))%mod;
        add[son]=(add[son]+addrt)%mod;
    }
}

void push_down(int rt,int l,int r){
    push_son(ls[rt],l,ml,cov[rt],mul[rt],add[rt]);
    push_son(rs[rt],mr,r,cov[rt],mul[rt],add[rt]);
    cov[rt]=-1; mul[rt]=1; add[rt]=0;
}

void push_up(int rt,int l,int r){
    sum[0][rt]=(sum[0][ls[rt]]+sum[0][rs[rt]])%mod;
    sum[1][rt]=(sum[1][ls[rt]]+sum[1][rs[rt]])%mod;
    sum[2][rt]=(sum[2][ls[rt]]+sum[2][rs[rt]])%mod;
}

void build(int&rt,int l,int r){
    rt=tot=0;
    push_son(rt,l,r,-1,1,0);
}

void update(int rt,int l,int r,int ql,int qr,int d,int type){
    if(ql<=l&&r<=qr){
        if(type==1) push_son(rt,l,r,-1,1,d);
        if(type==2) push_son(rt,l,r,-1,d,0);
        if(type==3) push_son(rt,l,r, d,1,0);
    }
    else{
        push_down(rt,l,r);
        if(ml>=ql) update(ls[rt],l,ml,ql,qr,d,type);
        if(mr<=qr) update(rs[rt],mr,r,ql,qr,d,type);
        push_up(rt,l,r);
    }
}

int query(int rt,int l,int r,int ql,int qr,int pw){// pw-1
    if(ql<=l&&r<=qr){
        return sum[pw][rt];
    }
    else{
        push_down(rt,l,r);
        int ret=0;
        if(ml>=ql) ret+=query(ls[rt],l,ml,ql,qr,pw);
        if(mr<=qr) ret+=query(rs[rt],mr,r,ql,qr,pw);
        return ret%mod;
    }
}