• 线段树、主席树


    ctsc2018的D2T1(主席树模板题),大家都半个小时AC了,我因为一个sb bug调了2个多小时……

    博主是个大sb。

    bzoj2653 middle

    一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整。

    给你一个长度为n的序列s。回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。

    其中a<b<c<d。位置也从0开始标号。强制在线。

    如果询问只有一个,我们当然可以二分答案,把$geq mid$的置为1,其他置为-1,

    然后就是求满足左端点在$[a,b]$之间,右端点在$[c,d]$之间的最大权值的子序列的权值是否$geq 0$

    那么对于$[b,c]$之间的所有数,是肯定要选的,那么$[a,b-1]$的最大后缀、$[b,c]$、$[c+1,d]$的最大前缀拼起来就是答案

    可以用线段树

    对于多组询问,我们肯定不能每次二分一个答案就把所有点权都重置一遍

    所以就用主席树,第$i$棵树是二分答案的$mid=i$时查找的线段树,就是$<i$的点权都是-1

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    #include<vector>
    using namespace std;
    #define ll long long
    #define db double
    #define For(i,a,b) for(int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(int i=(a);i>=(b);--i)
    #define lc son[pos][0]
    #define rc son[pos][1]
    const int maxn=2e4+7,maxm=1e7+7;
    int n,m,a[maxn],p[maxn],TOT,tot,ans;
    vector<int> G[maxn];
     
    char cc;ll ff;
    template<typename T>void read(T& aa) {
        aa=0;ff=1; cc=getchar();
        while(cc!='-'&&(cc<'0'||cc>'9')) cc=getchar();
        if(cc=='-') ff=-1,cc=getchar();
        while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
        aa*=ff;
    }
     
    struct Node{
        int ld,rd,sum;
        Node(){}
        Node(int ld,int rd,int sum):ld(ld),rd(rd),sum(sum){}
        Node operator + (const Node& b) {
            Node o;
            o.ld=max(ld,sum+b.ld);
            o.rd=max(b.rd,rd+b.sum);
            o.sum=sum+b.sum;
            return o;
        }
    }node[maxm];
     
    int son[maxm][2];
    int ql,qr,qx;
    void get_bld(int pos,int l,int r) {
        if(l==r) {
            node[pos]=Node(1,1,1);
            return;
        }
        int mid=(l+r)>>1;
        get_bld(lc=++tot,l,mid);
        get_bld(rc=++tot,mid+1,r);
        node[pos]=node[lc]+node[rc];
    }
     
    void bld(int& pos,int last,int l,int r) {
        if(!pos) {
            pos=++tot;
            lc=son[last][0];
            rc=son[last][1];
        }   
        if(l==r) {
            node[pos]=Node(0,0,-1);
            return;
        }
        int mid=(l+r)>>1;
        if(qx<=mid) {
            if(lc==son[last][0]) lc=0;
            bld(lc,son[last][0],l,mid);
        }
        else {
            if(rc==son[last][1]) rc=0;
            bld(rc,son[last][1],mid+1,r);
        }
        node[pos]=node[lc]+node[rc];
    }
     
    Node q(int pos,int l,int r) {
        if(l>=ql&&r<=qr) return node[pos];
        int mid=(l+r)>>1;
        if(qr<=mid) return q(lc,l,mid);
        if(ql>mid) return q(rc,mid+1,r);
        return q(lc,l,mid)+q(rc,mid+1,r);
    }
     
    bool check(int x,int l1,int r1,int l2,int r2) {
        Node L,O,R; L=R=O=Node(0,0,0);
        ql=l1; qr=r1-1;
        L=q(x,1,n);
        ql=l2+1; qr=r2;
        R=q(x,1,n);
        ql=r1; qr=l2;
        O=q(x,1,n);
        return L.rd+O.sum+R.ld>=0;
    }
     
    int get_ans(int x,int y,int z,int w) {
        if(x>y) swap(x,y); if(y>z) swap(y,z); if(z>w) swap(z,w);
        if(x>y) swap(x,y); if(y>z) swap(y,z); if(x>y) swap(x,y);
        int l1=x,r1=y,l2=z,r2=w;
    //  printf("get_ans:%d~%d,%d~%d
    ",l1,r1,l2,r2);
        int l=1,r=TOT,mid;
        if(check(r,l1,r1,l2,r2)) return r;
        while(l<r-1) {
            mid=(l+r)>>1;
            if(check(mid,l1,r1,l2,r2)) l=mid;
            else r=mid;
        }
        return l;
    }
     
    int main() {
        read(n); int x,y,z,w;
        For(i,1,n) read(a[i]),p[i]=a[i];
        sort(p+1,p+n+1);
        TOT=unique(p+1,p+n+1)-(p+1);
        For(i,1,n) a[i]=lower_bound(p+1,p+TOT+1,a[i])-p;
        For(i,1,n) G[a[i]].push_back(i);
        tot=TOT; get_bld(1,1,n);
        For(i,2,TOT) {
            son[i][0]=son[i-1][0];
            son[i][1]=son[i-1][1];
            node[i]=node[i-1];
            x=G[i-1].size();
            For(j,0,x-1) {
                qx=G[i-1][j];
                bld(i,i-1,1,n);
            }
        }
        read(m);
        For(i,1,m) {
            read(x); read(y); read(z); read(w);
            x=(x+ans)%n+1; y=(y+ans)%n+1;
            z=(z+ans)%n+1; w=(w+ans)%n+1;
            ans=get_ans(x,y,z,w);
            printf("%d
    ",ans=p[ans]);
        }
        return 0;
    }
    

    bzoj3524 Couriers

    给一个长度为$n$的序列$a$。$1 leq a[i] leq n$。

    $m$组询问,每次询问一个区间$[l,r]$,是否存在一个数在$[l,r]$中出现的次数大于$(r-l+1)/2$。如果存在,输出这个数,否则输出0。

    一个数,如果满足条件,那么他一定是中位数,所以直接找区间的中位数,然后再查询它出现次数

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define ll long long
    #define db double
    #define For(i,a,b) for(int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(int i=(a);i>=(b);--i)
    #define lc son[pos][0]
    #define rc son[pos][1]
    const int maxn=5e5+7,maxm=1e7+7;
    int n,m,tot;
     
    char cc;ll ff;
    template<typename T>void read(T& aa) {
        aa=0;ff=1; cc=getchar();
        while(cc!='-'&&(cc<'0'||cc>'9')) cc=getchar();
        if(cc=='-') ff=-1,cc=getchar();
        while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
        aa*=ff;
    }
     
    int sum[maxm],son[maxm][2],qx,qy;
    void bld(int pos,int last,int l,int r) {
        sum[pos]=sum[last]+1;
        if(l==r) return;
        int mid=(l+r)>>1;
        if(qx<=mid) rc=son[last][1],bld(lc=++tot,son[last][0],l,mid);
        else lc=son[last][0],bld(rc=++tot,son[last][1],mid+1,r);
    }
     
    int q(int ld,int rd,int l,int r) {
        if(l==r) {
            if(sum[rd]-sum[ld]>=qy) return l;
            return 0;
        }
        int mid=(l+r)>>1,x=sum[son[rd][0]]-sum[son[ld][0]];
        if(x>=qx) return q(son[ld][0],son[rd][0],l,mid);
        qx-=x;
        return q(son[ld][1],son[rd][1],mid+1,r);
    }
     
    int main() {
        read(n); read(m); tot=n;
        For(i,1,n) {
            read(qx); 
            bld(i,i-1,1,n);
        }
        int x,y;
        For(i,1,m) {
            read(x); read(y); qx=qy=(y-x+1)/2+1;
            printf("%d
    ",q(x-1,y,1,n));
        }
        return 0;
    }
    

    bzoj3585 mex

    有一个长度为n的数组{a1,a2,...,an}。m次询问,每次询问一个区间内最小没有出现过的自然数。

    $n,m leq 2*10^5$

    主席树

    第$i$棵线段树(权值线段树),表示的是我们如果只考虑数组的前i个数,那么每个数$x$出现的最大位置$f(x)$是在哪,维护区间最小值

    对于询问$(l,r)$,我们在第$r$棵线段树上二分,找到最大的$p$,使得$min(f(1),f(2),...,f(p-1)) geq l$

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define ll long long
    #define db double
    #define For(i,a,b) for(int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(int i=(a);i>=(b);--i)
    #define lc son[pos][0]
    #define rc son[pos][1]
    const int maxn=2e5+7,maxm=1e7+7;
    int n,m,W,tot;
     
    char cc;ll ff;
    template<typename T>void read(T& aa) {
        aa=0;ff=1; cc=getchar();
        while(cc!='-'&&(cc<'0'||cc>'9')) cc=getchar();
        if(cc=='-') ff=-1,cc=getchar();
        while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
        aa*=ff;
    }
     
    int num[maxm],son[maxm][2],ql,qr,qx;
    void bld(int pos,int last,int l,int r) {
        if(l==r) {num[pos]=qx;return;}
        int mid=(l+r)>>1;
        if(ql<=mid) rc=son[last][1],bld(lc=++tot,son[last][0],l,mid);
        else lc=son[last][0],bld(rc=++tot,son[last][1],mid+1,r);
        num[pos]=min(num[lc],num[rc]);
    }
     
    int q(int pos,int l,int r) {
        if(l==r) return l;
        int mid=(l+r)>>1;
        if(num[lc]<qx) return q(lc,l,mid);
        return q(rc,mid+1,r);
    }
     
    int main() {
        read(n); read(m); W=n+1; tot=n;
        int x,y;
        For(i,1,n) {
            read(x); ++x;
            if(x>n) {
                son[i][0]=son[i-1][0];
                son[i][1]=son[i-1][1];
                continue;
            }
            ql=qr=x; qx=i;
            bld(i,i-1,1,W);
        }
        For(i,1,m) {
            read(x); read(y); qx=x;
            printf("%d
    ",q(y,1,W)-1);
        }
        return 0;
    }
    

    bzoj3123 森林

    小Z有一片森林,含有$N$个节点,每个节点上都有一个非负整数作为权值。初始的时候,森林中有$M$条边。

    小Z希望执行$T$个操作,操作有两类:

    Q x y k查询点$x$到点$y$路径上所有的权值中,第$k$小的权值是多少。此操作保证点$x$和点$y$连通,同时这两个节点的路径上至少有$k$个点。

    L x y在点$x$和点$y$之间连接一条边。保证完成此操作后,仍然是一片森林。

    为了体现程序的在线性,我们把输入数据进行了加密。设$lastans$为程序上一次输出的结果,初始的时候$lastans$为0。

    对于一个输入的操作Q x y k,其真实操作为Q x^lastans y^lastans k^lastans。

    对于一个输入的操作L x y,其真实操作为L x^lastans y^lastans。其中^运算符表示异或,等价于pascal中的xor运算符。

    请写一个程序來帮助小Z完成这些操作。

    $N,M,T leq 8*10^4$

    主席树+启发式合并,每颗线段树维护到根的路径的信息,每次合并时,直接dfs,顺便插入线段树。

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define ll long long
    #define db double
    #define For(i,a,b) for(int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(int i=(a);i>=(b);--i)
    #define lc son[pos][0]
    #define rc son[pos][1]
    const int maxn=8e4+7,maxm=1e7+7,maxt=23,W=19;
    int Td,n,m,Q,w[maxn],p[maxn],TOT,tot,ans;
    char s[17];
     
    char cc;ll ff;
    template<typename T>void read(T& aa) {
        aa=0;ff=1; cc=getchar();
        while(cc!='-'&&(cc<'0'||cc>'9')) cc=getchar();
        if(cc=='-') ff=-1,cc=getchar();
        while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
        aa*=ff;
    }
     
    int f[maxn],size[maxn];
    int find(int x) {return x==f[x]? x:f[x]=find(f[x]);}
     
    int fir[maxn],nxt[2*maxn],to[2*maxn],e=0;
    void add(int x,int y) {
        to[++e]=y;nxt[e]=fir[x];fir[x]=e;
        to[++e]=x;nxt[e]=fir[y];fir[y]=e;
    }
     
    int root[maxn],sum[maxm],son[maxm][2],ql,qr,qx;
    void bld(int&pos,int last,int l,int r) {
        pos=++tot; sum[pos]=sum[last]+1;
        if(l==r) return;
        int mid=(l+r)>>1;
        if(qx<=mid) rc=son[last][1],bld(lc,son[last][0],l,mid);
        else lc=son[last][0],bld(rc,son[last][1],mid+1,r);
    }
     
    int q(int p1,int p2,int p3,int p4,int l,int r) {
        if(l==r) return l;
        int mid=(l+r)>>1,x;
        x=sum[son[p1][0]]+sum[son[p2][0]]-sum[son[p3][0]]-sum[son[p4][0]];
        if(qx<=x) return q(son[p1][0],son[p2][0],son[p3][0],son[p4][0],l,mid);
        qx-=x; return q(son[p1][1],son[p2][1],son[p3][1],son[p4][1],mid+1,r);
    } 
     
    int fa[maxn][maxt],dep[maxn];
    void dfs(int pos,int f) {
        fa[pos][0]=f; dep[pos]=dep[f]+1;
        For(i,1,W) fa[pos][i]=fa[fa[pos][i-1]][i-1];
        qx=w[pos]; bld(root[pos],root[f],1,TOT);
        int y,z;
        for(y=fir[pos];y;y=nxt[y]) {
            if((z=to[y])==f) continue;
            dfs(z,pos);
        }
    }
     
    int get_lca(int x,int y) {
        if(dep[x]!=dep[y]) {
            if(dep[x]<dep[y]) swap(x,y);
            Rep(i,W,0) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
        }
        if(x==y) return x;
        Rep(i,W,0) if(fa[x][i]!=fa[y][i]){
            x=fa[x][i]; y=fa[y][i];
        }
        return fa[x][0];
    }
     
    int Yth(int x,int y,int k) {
        int lca=get_lca(x,y),rs; qx=k;
        rs=q(root[x],root[y],root[lca],root[fa[lca][0]],1,TOT);
        return p[rs];
    }
     
    void lk(int x,int y) {
        add(x,y);
        int a=find(x),b=find(y);
        if(size[a]<size[b]) swap(a,b),swap(x,y);
        size[a]+=size[b]; f[b]=a;
        dfs(y,x);
    }
     
    int main() {
        read(Td); int x,y,k,a,b;
        read(n); read(m); read(Q);
        For(i,1,n) read(w[i]),p[i]=w[i];
        sort(p+1,p+n+1);
        TOT=unique(p+1,p+n+1)-(p+1);
        For(i,1,n) w[i]=lower_bound(p+1,p+TOT+1,w[i])-p;
        For(i,1,n) f[i]=i,size[i]=1;
        For(i,1,m) {
            read(x); read(y);
            add(x,y);
            a=find(x); b=find(y);
            size[a]+=size[b]; f[b]=a;
        }
        For(i,1,n) if(find(i)==i) dfs(i,0);
        For(i,1,Q) {
            scanf("%s",s+1); read(x); read(y);
            x^=ans; y^=ans;
            if(s[1]=='Q') {
                read(k); k^=ans;
                printf("%d
    ",ans=Yth(x,y,k));
            }
            else lk(x,y);
        }
        return 0;
    }
    

      

    rehealsal

    给出$n$个三元组 $e[i]=(s_i , t_i , w_i)$

    第i个三元组的价值为 $sum w_j$ ,$j$ 满足以下4个条件:

    1、$j<i$

    2、$t_j<t_i$

    3、$s_j<s_i$

    4、不存在$j<k<i$,且$sj<sk<si$

    xxy大佬的题解(http://www.cnblogs.com/TheRoadToTheGold/p/8718239.html):

    把每个三元组看作二维平面上的一个点$(i,s_i)$

    先不考虑$t$,

    那么$j$若满足要求,必须满足以$(j,s_j)$为左下角,以$(i,s_i)$为右上角的矩形内没有其他的三元组

    可以用CDQ分治解决

    设三元组$e[i]$的坐标为$(x,y)=(i,s_i)$

    先将所有的三元组按$y$排序,然后按$x$归并

    即左右两边归并时,左边所有三元组的$y$小于右边所有三元组的$y$

    归并结束后,左右两边合并为$x$递增的集合

    考虑左边对右边的贡献

    在归并的过程中维护两个单调栈$L$和$R$

    栈$L$ 维护左边的三元组,满足$x$单调递增,$y$单调递减

    栈$R$ 维护右边的三元组,满足$x$单调递增,$y$单调递增,且栈顶的$y$一定小于当前的$y$

    对于右边的一个三元组$j$,左边对其有贡献的三元组$i$满足

    1、$i<j$,因为是按$x$归并,所以此条件一定满足

    2、$i$在栈$L$中,如果$i$不在栈$L$中,说明$i$后面,$j$前面存在一个$k$,满足$s_i<s_k<s_j$

    3、设栈$r$的栈顶为$k$,$i>k$,否则这个$k$会使 $i<k<j$ 且$s_i<s_k<s_j$

    我们只维护栈$L$中三元组的信息,即可满足条件2

    至于条件3,因为栈$L$的$x$单调递增,二分查找第一个满足条件的,那么它到栈$L$的栈顶都满足条件

    记录栈$L$中$w$的前缀和即可解决

    现在再考虑$t$,只需要将前缀和改为可持久化权值线段树即可

    bzoj2333 棘手的操作

    有N个节点,标号从1到N,这N个节点一开始相互不连通。第i个节点的初始权值为a[i],接下来有如下一些操作:

    U x y: 加一条边,连接第x个节点和第y个节点
    A1 x v: 将第x个节点的权值增加v
    A2 x v: 将第x个节点所在的连通块的所有节点的权值都增加v
    A3 v: 将所有节点的权值都增加v
    F1 x: 输出第x个节点当前的权值
    F2 x: 输出第x个节点所在的连通块中,权值最大的节点的权值
    F3: 输出所有节点中,权值最大的节点的权值

    $N ,Q leq 3*10^5$

    做法1:线段树。把会在同一连通块的在线段树上排在一起

    做法2:左偏树。两种:第一种是每个连通块,第二种是所有第一种堆的堆顶

    线段树:

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define ll long long
    #define db double
    #define For(i,a,b) for(int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(int i=(a);i>=(b);--i)
    const int maxn=3e5+7,INF=0x3f3f3f3f;
    int n,m,a[maxn],p[maxn],id[maxn];
    char s[17];
     
    char cc;ll ff;
    template<typename T>void read(T& aa) {
        aa=0;ff=1; cc=getchar();
        while(cc!='-'&&(cc<'0'||cc>'9')) cc=getchar();
        if(cc=='-') ff=-1,cc=getchar();
        while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
        aa*=ff;
    }
     
    int f[maxn],end[maxn],nxt[maxn];
    int find(int x) {return x==f[x]? x:f[x]=find(f[x]);}
     
    void lk(int x,int y) {
        x=find(x); y=find(y);
        if(x==y) return;
        f[y]=x; nxt[end[x]]=y; end[x]=end[y];
    }
     
    struct Act{
        int op,x,y;
        Act(){}
        Act(int op,int x,int y):op(op),x(x),y(y){}
    }act[maxn];
     
    int num[4*maxn],laz[4*maxn],ql,qr,qx;
    void ud(int pos) {num[pos]=max(num[pos<<1],num[pos<<1|1]);}
    void pd(int pos) {
        if(!laz[pos]) return;
        num[pos<<1]+=laz[pos]; num[pos<<1|1]+=laz[pos];
        laz[pos<<1]+=laz[pos]; laz[pos<<1|1]+=laz[pos];
        laz[pos]=0;
    }   
     
    void bld(int pos,int l,int r) {
        if(l==r) {
            num[pos]=a[p[l]];
            return;
        }
        int mid=(l+r)>>1;
        bld(pos<<1,l,mid); bld(pos<<1|1,mid+1,r);
        ud(pos);
    }
     
    void chge(int pos,int l,int r) {
        if(l>=ql&&r<=qr) {
            num[pos]+=qx;
            laz[pos]+=qx;
            return;
        }
        int mid=(l+r)>>1; pd(pos);
        if(ql<=mid) chge(pos<<1,l,mid);
        if(qr>mid) chge(pos<<1|1,mid+1,r);
        ud(pos);
    }
     
    int q(int pos,int l,int r) {
        if(l>=ql&&r<=qr) return num[pos];
        int mid=(l+r)>>1,rs=-INF; pd(pos);
        if(ql<=mid) rs=max(rs,q(pos<<1,l,mid));
        if(qr>mid) rs=max(rs,q(pos<<1|1,mid+1,r));
        return rs;
    }
     
    int main() {
        read(n);
        For(i,1,n) read(a[i]),f[i]=end[i]=i;
        read(m); int op,x,y;
        For(i,1,m) {
            scanf("%s",s+1); x=y=0;
            if(s[1]=='U') op=0;
            else if(s[1]=='A') op=s[2]-'0';
            else op=s[2]-'0'+3;
            if(op==0||op%3!=0) read(x);
            if(op<=3) read(y);
            act[i]=Act(op,x,y);
            if(op==0) lk(x,y);
        }
        y=0;
        For(i,1,n) if(i==find(i)) 
            for(x=i;x;x=nxt[x]) p[id[x]=++y]=x;
        bld(1,1,n);
        For(i,1,n) f[i]=end[i]=i,nxt[i]=0;
        For(i,1,m) {
            op=act[i].op; x=act[i].x; y=act[i].y;
            if(op==0) lk(x,y);
            else {
                qx=y;
                if(op%3==1) ql=qr=id[x];
                else if(op%3==2) x=find(x),ql=id[x],qr=id[end[x]];
                else ql=1,qr=n;
                if(op<=3) chge(1,1,n);
                else printf("%d
    ",q(1,1,n));
            }
        }
        return 0;
    }
    

    bzoj5017 炸弹

    在一条直线上有 $N$ 个炸弹,每个炸弹的坐标是 $X_i$,爆炸半径是 $R_i$,当一个炸弹爆炸时,如果另一个炸弹所在位置 $X_j$ 满足:
    $X_i-R_i leq X_j leq X_i+R_i$,那么,该炸弹也会被引爆。

    现在,请你帮忙计算一下,先把第$i$个炸弹引爆,将引爆多少个炸弹呢?

    输入保证$X_i$严格递增。

    $N leq 5*10^5 , |X_i| leq 10^{18} , R_i leq 2*10^{18}$

    对于一个炸弹引爆之后,首先引爆的炸弹,是一个区间,最后所有被引爆的炸弹也是一个区间

    所以我们就利用线段树优化建图,然后tarjan,然后就可以处理出每个点可以到达多少个点了

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define ll long long
    #define db double
    #define For(i,a,b) for(int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(int i=(a);i>=(b);--i)
    const ll mod=1e9+7;
    const int maxn=2e6+7,maxm=2e7+7;
    ll n,r[maxn],p[maxn],id[maxn],d[maxn],tot,ans;
    
    char cc;ll ff;
    template<typename T>void read(T& aa) {
    	aa=0;ff=1; cc=getchar();
    	while(cc!='-'&&(cc<'0'||cc>'9')) cc=getchar();
    	if(cc=='-') ff=-1,cc=getchar();
    	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
    	aa*=ff;
    }
    
    int fir[maxn],nxt[maxm],to[maxm],e=0;
    void add(int x,int y) {
    //	printf("add:%d->%d
    ",x,y);
    	to[++e]=y;nxt[e]=fir[x];fir[x]=e;
    }
    
    int FIR[maxn],NXT[maxm],TO[maxm],E=0,ind[maxn];
    void ADD(int x,int y) {
    //	printf("ADD:%d->%d
    ",x,y);
    	TO[++E]=y;NXT[E]=FIR[x];FIR[x]=E; ++ind[y];
    }
    
    int ld[maxn],rd[maxn],ql,qr,qx;
    void bld(int pos,int l,int r) {
    	d[pos]=++tot; ld[tot]=l; rd[tot]=r; 
    	if(l==r) {id[l]=tot;return;}
    	int mid=(l+r)>>1;
    	bld(pos<<1,l,mid); bld(pos<<1|1,mid+1,r);
    	add(d[pos],d[pos<<1]); add(d[pos],d[pos<<1|1]);
    }
    
    void chge(int pos,int l,int r) {
    	if(l>=ql&&r<=qr) {add(qx,d[pos]);return;}
    	int mid=(l+r)>>1;
    	if(ql<=mid) chge(pos<<1,l,mid);
    	if(qr>mid) chge(pos<<1|1,mid+1,r);
    }
    
    int dfn[maxn],low[maxn],dfn_clock,zz[maxn],inz[maxn],top;
    int bel[maxn],Ld[maxn],Rd[maxn],toth;
    void tj(int pos) {
    	dfn[pos]=low[pos]=++dfn_clock;
    	zz[++top]=pos;inz[pos]=1;
    	int y,z,bot=top;
    	for(y=fir[pos];y;y=nxt[y]) {
    		if(inz[z=to[y]]) low[pos]=min(low[pos],dfn[z]);
    		if(dfn[z]) continue;
    		tj(z); low[pos]=min(low[pos],low[z]);
    	}
    	if(dfn[pos]==low[pos]) {
    		++toth; Ld[toth]=ld[zz[bot]]; Rd[toth]=rd[zz[bot]];
    		For(i,bot,top) {
    			bel[zz[i]]=toth;inz[zz[i]]=0;
    			Ld[toth]=min(Ld[toth],ld[zz[i]]);
    			Rd[toth]=max(Rd[toth],rd[zz[i]]);
    		}
    		top=bot-1;
    	}
    }
    
    void topsort() {
    	int s=1,t=0,x,y,z;
    	For(i,1,toth) if(!ind[i]) zz[++t]=i;
    	while(s<=t) {
    		x=zz[s++];
    		for(y=FIR[x];y;y=NXT[y]) 
    			if((--ind[z=TO[y]])==0) zz[++t]=z;
    	}
    	Rep(i,toth,1) {
    		x=zz[i];
    		for(y=FIR[x];y;y=NXT[y]) {
    			z=TO[y];
    			Ld[x]=min(Ld[x],Ld[z]); Rd[x]=max(Rd[x],Rd[z]);
    		}
    	}
    }
    
    int main() {
    	read(n);
    	For(i,1,n) read(p[i]),read(r[i]);
    	bld(1,1,n);
    	For(i,1,n) {
    		ql=lower_bound(p+1,p+n+1,p[i]-r[i])-p;
    		qr=upper_bound(p+1,p+n+1,p[i]+r[i])-p; qr--;
    		qx=id[i];
    		chge(1,1,n);
    	}
    	tj(1); int x,y,z;
    	For(i,1,tot) {
    		x=bel[i];
    		for(y=fir[i];y;y=nxt[y]) {
    			if((z=bel[to[y]])==x) continue;
    			ADD(x,z);
    		}
    	}
    	topsort();
    	For(i,1,n) {
    		x=bel[id[i]];
    		ans+=(ll)i*(Rd[x]-Ld[x]+1)%mod;
    	}
    	printf("%lld
    ",ans%mod);
    	return 0;
    }
    

      

    bzoj4817 树点涂色

    Bob有一棵n个点的有根树,其中1号点是根节点。Bob在每个点上涂了颜色,并且每个点上的颜色不同。

    定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。Bob可能会进行这几种操作:

    1 x:把点x到根节点的路径上所有的点染上一种没有用过的新颜色。

    2 x y:求x到y的路径的权值。

    3 x y:在以x为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。

    Bob一共会进行m次操作

    $n,m leq 10^5$

    为什么学长yyh和SDdalao xxy的代码都200+,那么长呢……

    lct+线段树(dfs序)

    注意这道题每次都是染一种没有用过的新颜色,而且路径直接到根

    这种一般都是用线段树直接维护到根的路径,谁去树链剖分呢,查路径还多带一个log多不划算

    lct里面同一个splay中的点的颜色相同。

    1操作就是access,2操作是在线段树里面查x到根的+y到根的-lca到根的*2+1,而3是线段树查询最大值

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define ll long long
    #define db double
    #define For(i,a,b) for(int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(int i=(a);i>=(b);--i)
    #define lc son[pos][0]
    #define rc son[pos][1]
    const int maxn=1e5+7,maxt=23,W=19;
    int n,m,fa[maxn],son[maxn][2];
    
    char cc;ll ff;
    template<typename T>void read(T& aa) {
    	aa=0;ff=1; cc=getchar();
    	while(cc!='-'&&(cc<'0'||cc>'9')) cc=getchar();
    	if(cc=='-') ff=-1,cc=getchar();
    	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
    	aa*=ff;
    }
    /////////////////////////////////////////////////tree
    int fir[maxn],nxt[2*maxn],to[2*maxn],e=0;
    void add(int x,int y) {
    	to[++e]=y;nxt[e]=fir[x];fir[x]=e;
    	to[++e]=x;nxt[e]=fir[y];fir[y]=e;
    }
    
    int dfn[maxn],end[maxn],p[maxn],dep[maxn],dfn_clock;
    int F[maxn][maxt];
    void dfs(int pos,int f) {
    	dfn[pos]=++dfn_clock; p[dfn_clock]=pos;
    	fa[pos]=F[pos][0]=f; dep[pos]=dep[f]+1; 
    	For(i,1,W) F[pos][i]=F[F[pos][i-1]][i-1];
    	int y,z;
    	for(y=fir[pos];y;y=nxt[y]) {
    		if((z=to[y])==f) continue;
    		dfs(z,pos);
    	}
    	end[pos]=dfn_clock;
    }
    
    int get_lca(int x,int y) {
    	if(dep[x]!=dep[y]) {
    		if(dep[x]<dep[y]) swap(x,y);
    		Rep(i,W,0) if(dep[F[x][i]]>=dep[y]) x=F[x][i];
    	}
    	if(x==y) return x;
    	Rep(i,W,0) if(F[x][i]!=F[y][i]) {
    		x=F[x][i]; y=F[y][i];
    	}
    	return F[x][0];
    }
    /////////////////////////////////////////////////segment tree
    int num[4*maxn],laz[4*maxn],ql,qr,qx;
    void ud(int pos) {num[pos]=max(num[pos<<1],num[pos<<1|1]);}
    void pd(int pos) {
    	if(!laz[pos]) return;
    	num[pos<<1]+=laz[pos]; num[pos<<1|1]+=laz[pos];
    	laz[pos<<1]+=laz[pos]; laz[pos<<1|1]+=laz[pos];
    	laz[pos]=0;
    }
    
    void bld(int pos,int l,int r) {
    	if(l==r) {
    		num[pos]=dep[p[l]];
    		return;
    	}
    	int mid=(l+r)>>1;
    	bld(pos<<1,l,mid); bld(pos<<1|1,mid+1,r);
    	ud(pos);
    }
    
    void chge(int pos,int l,int r) {
    	if(l>=ql&&r<=qr) {
    		num[pos]+=qx;
    		laz[pos]+=qx;
    		return;
    	}
    	int mid=(l+r)>>1; pd(pos);
    	if(ql<=mid) chge(pos<<1,l,mid);
    	if(qr>mid) chge(pos<<1|1,mid+1,r);
    	ud(pos);
    }
    
    int q(int pos,int l,int r) {
    	if(l>=ql&&r<=qr) return num[pos];
    	int mid=(l+r)>>1,rs=0; pd(pos);
    	if(ql<=mid) rs=max(rs,q(pos<<1,l,mid));
    	if(qr>mid) rs=max(rs,q(pos<<1|1,mid+1,r));
    	return rs;
    }
    
    int Yth(int x,int y) {
    	int lca=get_lca(x,y),rs=1;
    	ql=qr=dfn[x]; rs+=q(1,1,n);
    	ql=qr=dfn[y]; rs+=q(1,1,n);
    	ql=qr=dfn[lca];rs-=2*q(1,1,n);
    	return rs;
    }
    /////////////////////////////////////////////////lct
    bool isroot(int pos) {return son[fa[pos]][0]!=pos&&son[fa[pos]][1]!=pos;}
    
    void rotate(int pos) {
    	int x,y,p; y=fa[x=fa[pos]]; p=son[x][1]==pos;
    	if(!isroot(x)) son[y][son[y][1]==x]=pos; fa[pos]=y;
    	son[x][p]=son[pos][!p]; fa[son[pos][!p]]=x;
    	son[pos][!p]=x; fa[x]=pos;
    }
    
    void splay(int pos) {
    	for(int x,y;!isroot(pos);rotate(pos)) {
    		y=fa[x=fa[pos]];
    		if(!isroot(x)) (son[x][1]==pos^son[y][1]==x)? rotate(pos):rotate(x);
    	}
    }
    
    int find(int pos) {
    	while(lc) pos=lc;
    	return pos;
    }
    
    void access(int pos) {
    	for(int t=0,p;pos;pos=fa[t=pos]) {
    		splay(pos);
    		if(rc) {
    			p=find(rc);
    			ql=dfn[p]; qr=end[p]; qx=1;
    //			printf("chge:%d,%d
    ",p,qx);
    			chge(1,1,n);
    		}
    		if(t) {
    			p=find(t);
    			ql=dfn[p]; qr=end[p]; qx=-1;
    //			printf("chge:%d,%d
    ",p,qx);
    			chge(1,1,n);
    		}
    		rc=t;
    	}
    }
    
    int main() {
    	read(n); read(m); int op,x,y;
    	For(i,1,n-1) {
    		read(x); read(y);
    		add(x,y);
    	}
    	dfs(1,0);
    	bld(1,1,n);
    	For(i,1,m) {
    		read(op); read(x);
    		if(op==1) access(x);
    		else if(op==2) read(y),printf("%d
    ",Yth(x,y));
    		else ql=dfn[x],qr=end[x],printf("%d
    ",q(1,1,n));
    	}
    	return 0;
    }
    

    bzoj4552 排序

    给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排序分为两种:
    (0,l,r)表示将区间[l,r]的数字升序排序
    (1,l,r)表示将区间[l,r]的数字降序排序
    最后询问第q位置上的数字。

    二分然后随便用啥子数据结构维护一下

    可以当作线段树合并模板题。

    不知道为什么要拿一个线段树来维护所有线段树,我懒,直接set维护算啦,代码短好多呐

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    #include<set>
    using namespace std;
    #define ll long long
    #define db double
    #define For(i,a,b) for(int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(int i=(a);i>=(b);--i)
    #define lc son[pos][0]
    #define rc son[pos][1]
    const int maxn=1e5+7,maxm=1e7+7;
    const ll Bs=25,U=(1<<25)-1;
    int n,m,root[maxn],fl[maxn],zz[maxn],t;
    set<int> G;
    set<int>::iterator it;
    
    char cc;ll ff;
    template<typename T>void read(T& aa) {
    	aa=0;ff=1; cc=getchar();
    	while(cc!='-'&&(cc<'0'||cc>'9')) cc=getchar();
    	if(cc=='-') ff=-1,cc=getchar();
    	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
    	aa*=ff;
    }
    
    ll pr(ll x,ll y) {return (x<<Bs)+y;}
    ll fi(ll x) {return x>>Bs;}
    ll se(ll x) {return x&U;}
    
    int sum[maxm],son[maxm][2],ql,qr,qx,tot;
    void ud(int pos) {sum[pos]=sum[lc]+sum[rc];}
    void bld(int&pos,int l,int r) {
    	if(!pos) pos=++tot;
    	sum[pos]+=qx;
    	if(l==r) return;
    	int mid=(l+r)>>1;
    	if(ql<=mid) bld(lc,l,mid);
    	else bld(rc,mid+1,r);
    }
    
    void merge(int&pos,int p,int l,int r) {
    	if((ll)pos*p==0) {pos=pos+p;return;}
    	if(l==r) {sum[pos]+=sum[p];return;}
    	int mid=(l+r)>>1;
    	merge(lc,son[p][0],l,mid);
    	merge(rc,son[p][1],mid+1,r);
    	ud(pos);
    }
    
    ll split(int pos,int l,int r,int k) {
    	int p=++tot,mid=(l+r)>>1; ll x;
    	if(sum[lc]==k) {son[p][1]=rc; rc=0;}
    	else if(sum[lc]<k) {
    		x=split(rc,mid+1,r,k-sum[lc]);
    		rc=fi(x); son[p][1]=se(x);
    	}
    	else {
    		x=split(lc,l,mid,k);
    		lc=fi(x); son[p][0]=se(x); son[p][1]=rc; rc=0;
    	}
    	ud(pos); ud(p);
    	return pr(pos,p);
    }
    
    void get_split(int pos,int x) {
    	if(x==0||x==sum[root[pos]]) return;
    	ll p;
    	if(!fl[pos]) p=split(root[pos],1,n,x);
    	else {
    		p=split(root[pos],1,n,sum[root[pos]]-x);
    		p=pr(se(p),fi(p)); root[pos]=fi(p);
    	}
    	root[pos+x]=se(p);
    	fl[pos+x]=fl[pos];
    	G.insert(pos+x);
    }
    
    int q(int pos,int l,int r) {
    	if(l==r) return l;
    	int mid=(l+r)>>1;
    	if(qx<=sum[lc]) return q(lc,l,mid);
    	qx-=sum[lc]; return q(rc,mid+1,r);
    }
    
    int get_ans(int x) {
    	int pos;
    	for(it=G.begin();it!=G.end();++it) {
    		if(x<=sum[root[*it]]) break;
    		x-=sum[root[*it]];
    	}
    	pos=*it;
    	if(!fl[pos]) qx=x;
    	else qx=sum[root[pos]]-x+1;
    	return q(root[pos],1,n);
    }
    /////////////////////////////////////////////////debug
    void dfs(int pos,int l,int r) {
    	if(!pos||(!sum[pos])) return;
    	if(l==r) {printf(" %d",l);return;}
    	int mid=(l+r)>>1;
    	dfs(lc,l,mid); dfs(rc,mid+1,r);
    }
    
    void debug() {
    	printf("G:
    ");
    	for(it=G.begin();it!=G.end();++it) {
    		printf("%d(fl=%d):",*it,fl[*it]);
    		dfs(root[*it],1,n); printf("
    ");
    	}
    	printf("
    ");
    }
    /////////////////////////////////////////////////
    int main() {
    	read(n); read(m); ll op,x,y,ld,rd;
    	For(i,1,n) root[i]=++tot,G.insert(i); G.insert(n+1);
    	For(i,1,n) read(x),ql=qr=x,qx=1,bld(root[i],1,n);
    	For(i,1,m) {
    		read(op); read(x); read(y);
    		it=G.upper_bound(x); --it; 
    		ld=*it; get_split(ld,x-ld);
    		it=G.upper_bound(y); --it; 
    		rd=*it; get_split(rd,y-rd+1);
    		t=0; for(it=G.lower_bound(x);*it<=rd;++it) zz[++t]=*it;
    		For(i,2,t) merge(root[zz[1]],root[zz[i]],1,n);
    		it=G.upper_bound(x); while(*it<=rd) G.erase(it),it=G.upper_bound(x);
    		fl[x]=op;
    //		debug();
    	}
    	read(x);
    	printf("%d
    ",get_ans(x));
    	return 0;
    }
    

    loj2537Minimax

    (C) 有一棵 (n) 个结点的有根树,根是 (1) 号结点,且每个结点最多有两个子结点。

    定义结点 (x) 的权值为:

    1.若 (x) 没有子结点,那么它的权值会在输入里给出,保证这类点中每个结点的权值互不相同

    2.若 (x) 有子结点,那么它的权值有 (p_x) 的概率是它的子结点的权值的最大值,有 (1-p_x) 的概率是它的子结点的权值的最小值。

    现在小 (C) 想知道,假设 (1) 号结点的权值有 (m) 种可能性,权值第 (i)的可能性的权值是 (V_i) ,它的概率为 (Di(Di>0)) ,求:

    [displaystyle sum _{i=1} ^ {m} i cdot V_i cdot D_i^2]

    你需要输出答案对 (998244353) 取模的值。

    对于 (40\%) 的数据,有 (1leq nleq 5000)

    对于 (100\%) 的数据,有 (1leq nleq 3 imes 10^5, 1leq w_ileq 10^9)

    线段树合并优化dp,sb真的错误太多。一定要记得pd和ud啊

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define ll long long
    #define db double
    #define For(i,a,b) for(int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(int i=(a);i>=(b);--i)
    #define lc son[pos][0]
    #define rc son[pos][1]
    const ll mod=998244353,R=796898467;
    const int maxn=1e6+7,maxm=2e7+7;
    ll n,fa[maxn],tson[maxn],v[maxn],p[maxn],TOT;
    int troot;
    
    char cc;ll ff;
    template<typename T>void read(T& aa) {
    	aa=0;ff=1; cc=getchar();
    	while(cc!='-'&&(cc<'0'||cc>'9')) cc=getchar();
    	if(cc=='-') ff=-1,cc=getchar();
    	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
    	aa*=ff;
    }
    
    int fir[maxn],nxt[maxn],to[maxn],e=0;
    void add(int x,int y) {
    //	printf("add:%d->%d
    ",x,y);
    	to[++e]=y;nxt[e]=fir[x];fir[x]=e;
    }
    
    int root[maxn],son[maxm][2],tot;
    ll sum[maxm],laz[maxm],ql,qr,qx;
    void ud(int pos) {sum[pos]=(sum[lc]+sum[rc])%mod;}
    void add_laz(int pos,ll x) {
    	laz[pos]=laz[pos]*x%mod;
    	sum[pos]=sum[pos]*x%mod;
    }
    void pd(int pos) {
    	if(laz[pos]==1) return;
    	add_laz(lc,laz[pos]);
    	add_laz(rc,laz[pos]);
    	laz[pos]=1;
    }
    
    void bld(int& pos,int l,int r) {
    	if(!pos) pos=++tot,laz[pos]=1;
    	sum[pos]+=qx;
    	if(l==r) return;
    	int mid=(l+r)>>1;
    	if(ql<=mid) bld(lc,l,mid);
    	if(qr>mid) bld(rc,mid+1,r);
    }
    
    void get_laz(int pos,int p,int l,int r,ll x,ll y) {
    	if((!pos)||(!p)||(l==r)) {
    		add_laz(pos,x);
    		add_laz(p,y);
    		return;
    	}
    	int mid=(l+r)>>1; pd(pos); pd(p);
    	ll l1=sum[lc],r1=sum[rc],l2=sum[son[p][0]],r2=sum[son[p][1]];
    	get_laz(lc,son[p][0],l,mid,(x+r2*(1-qx+mod))%mod,(y+r1*(1-qx+mod))%mod);
    	get_laz(rc,son[p][1],mid+1,r,(x+l2*qx)%mod,(y+l1*qx)%mod);
    	ud(pos); ud(p);
    }
    
    void merge(int&pos,int p,int l,int r) {
    	if((!pos)||(!p)) {pos=pos+p;return;}
    	if(l==r) {sum[pos]+=sum[p];return;}
    	pd(pos); pd(p);
    	int mid=(l+r)>>1;
    	merge(lc,son[p][0],l,mid);
    	merge(rc,son[p][1],mid+1,r);
    	ud(pos);
    }
    
    void get_ans(int pos) {
    	if(pos==0||tson[pos]==0) return;
    	int ls=to[fir[pos]],rs=to[nxt[fir[pos]]];
    	get_ans(ls); get_ans(rs);
    	qx=v[pos];
    	if(ls==0||rs==0) root[pos]=root[ls+rs];
    	else {
    		get_laz(root[ls],root[rs],1,TOT,0,0);
    		merge(root[ls],root[rs],1,TOT);
    	}
    	root[pos]=root[ls]; 
    }
    
    ll cal(int pos,int l,int r) {
    	if(l==r) return (ll)l*sum[pos]%mod*sum[pos]%mod*p[l]%mod;
    	pd(pos);
    	int mid=(l+r)>>1;
    	return (cal(lc,l,mid)+cal(rc,mid+1,r))%mod;
    }
    
    int main() {
    	read(n); read(fa[1]);
    	For(i,2,n) {
    		read(fa[i]); add(fa[i],i);
    		++tson[fa[i]];
    	}
    	For(i,1,n) {
    		read(v[i]);
    		if(!tson[i]) p[++TOT]=v[i];
    	}
    	sort(p+1,p+TOT+1);
    	For(i,1,n) {
    		if(!tson[i]) v[i]=lower_bound(p+1,p+TOT+1,v[i])-p;
    		else v[i]=v[i]*R%mod;
    	}
    	laz[0]=1;
    	For(i,1,n) if(!tson[i]) ql=qr=v[i],qx=1,bld(root[i],1,TOT);
    	get_ans(1);
    	printf("%lld
    ",cal(root[1],1,TOT));
    	return 0;
    }
    

      

  • 相关阅读:
    Lc40_组合总和II
    Spring整合ZooKeeper基础使用介绍
    常见Bean拷贝框架下划线驼峰互转扩展支持
    ElastchSearch 基本使用姿势
    Java中两种分页遍历的使用姿势
    【SpringBoot DB系列】Mybatis多数据源配置与使用
    【SpringBoot DB 系列】Mybatis-Plus 多数据源配置
    【SpringBoot DB 系列】Mybatis 基于 AbstractRoutingDataSource 与 AOP 实现多数据源切换
    【基础系列】ConfigurationProperties 配置绑定中那些你不知道的事情
    Spring 工具类之基本元素判断
  • 原文地址:https://www.cnblogs.com/Serene-shixinyi/p/9095513.html
Copyright © 2020-2023  润新知