• 【题解】Subsequence [SWTR-05] [P]


    【题解】Subsequence [SWTR-05] [P]

    传送门:( ext{Subsequence [SWTR-05] [P]})

    【题目描述】

    给出一个长为 (n) ((nleqslant 2*10^5)) 的序列 (a) ((a_ileqslant 10^9)) 。有 (m) ((mleqslant 2*10^5)) 次询问,每次询问给出 (L,R,V),求 (sum_{Lleqslant ileqslant jleqslant R}[max(i,j) leq V]left(sum_{k=i}^{j}a_k ight)),其中 (max(i,j)) 表示区间 ([i,j]) 的最大值。答案对 (2^{32}) 取模。

    【分析】

    先把 (a_i) 从大到小排好序,然后对询问离线,按 (V) 从大到小依次处理。

    对于每次询问,大于 (V) 的点将原序列剖成了若干个连续小区间。把连续小区间与 ([L,R]) 取并,得到的连续段的所有子区间均可对答案产生贡献,分别计算这些段的贡献再求和即为答案。

    连续段显然可以用平衡树维护(类似 ( ext{ODT}) 的感觉。只是这里只保留了值小于等于 (V) 的段),每次加入点 ((i,a_i)) 就相当于把一个连续段剖成了两半。询问时对于完全包含在 ([L,R]) 以内的段就在 ( ext{Spaly}) 上维护一下节点权值和,两个端点所在段则单独计算。

    考虑如何快速计算一个连续段 ([l,r]) 的贡献,也就是这个柿子: (calc(l,r)=sum_{i=l}^{r}sum_{j=i}^{r}S(j)-S(i-1)),其中 (S)(a) 的前缀和。

    化简得:(calc(l,r)=left(sum_{j=l}^{r}S(j)(j-l+1) ight)-left(sum_{i=l}^{r}S(i-1)(r-i+1) ight))

    预处理一个 (S_1(n)=sum_{i=1}^{n}iS(i),) (S_2(n)=sum_{i=1}^{n}S(i)),显然可以 (O(1)) 得到上面的柿子。

    时间复杂度:(O((n+m)log n))

    【Code】

    #include<algorithm>
    #include<iostream>
    #include<cstdio>
    #include<queue>
    #define LL unsigned int
    #define Re register LL
    using namespace std;
    const int N=2e5+10;
    LL n,T,A[N],S[N],S1[N],S2[N],Ans[N];
    struct QAQ{LL v,l,r,id;inline bool operator<(const QAQ &O)const{return v>O.v;}}a[N],Q[N];
    inline void in(Re &x){
        int f=0;x=0;char c=getchar();
        while(c<'0'||c>'9')f|=c=='-',c=getchar();
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=f?-x:x;
    }
    inline void print(Re x){if(x>9)print(x/10);putchar(x%10+'0');}
    inline LL ask1(Re l,Re r){return l<=r&&r>=1?S1[r]-(l?S1[l-1]:0):0;}
    inline LL ask2(Re l,Re r){return l<=r&&r>=1?S2[r]-(l?S2[l-1]:0):0;}
    inline LL calc(Re l,Re r){return l<=r?ask1(l,r)-(l-1)*ask2(l,r)-r*ask2(l-1,r-1)+ask1(l-1,r-1):0;}//计算原序列中区间[l,r]的贡献
    struct Splay{
        #define pl (tr[p].ps[0])
        #define pr (tr[p].ps[1])
        #define pf (tr[p].fa)
        #define pv (tr[p].v)
        LL O,root,Q[N];queue<LL>D;
        struct QAQ{LL l,r,v,S,rr,fa,size,ps[2];}tr[N];
        inline void pushup(Re p){
            tr[p].S=tr[pl].S+tr[pr].S+pv;
            tr[p].rr=pr?tr[pr].rr:tr[p].r;
            tr[p].size=tr[pl].size+tr[pr].size+1;
        }
        inline LL which(Re p){return tr[pf].ps[1]==p;}
        inline void connect(Re p,Re fa,Re o){tr[pf=fa].ps[o]=p;}
        inline void rotate(Re p){
            Re fa=pf,fas=which(p);
            Re pa=tr[fa].fa,pas=which(fa);
            Re x=tr[p].ps[fas^1];
            connect(x,fa,fas),connect(fa,p,fas^1),connect(p,pa,pas);
            pushup(fa),pushup(p);
        }
        inline void splay(Re p,Re to){
            for(Re fa;pf!=to;rotate(p))
                if(tr[fa=pf].fa!=to)rotate(which(p)==which(fa)?fa:p);
            if(!to)root=p;
        }
        inline void CL(Re p){
            tr[p].size=tr[p].S=tr[p].l=tr[p].r=tr[p].rr=pv=pf=pl=pr=0;
        }
        inline LL New(){
            Re p;
            if(D.empty())p=++O;
            else p=D.front(),D.pop();
            CL(p),tr[p].size=1;return p;
        }
        inline void build0(Re &p,Re l,Re r){//建初始区间{1,n}时在下面挂一个{0,0}和{n+1,n+1},方便后面split
            p=New(),tr[p].l=l,tr[p].r=r,pv=calc(l,r);
            pl=New(),tr[pl].l=tr[pl].r=0,tr[pl].v=0,tr[pl].fa=p,pushup(pl);
            pr=New(),tr[pr].l=tr[pr].r=n+1,tr[pr].v=0,tr[pr].fa=p,pushup(pr);
            pushup(p);
        }
        inline void build(Re &p,Re l,Re r){
            p=New(),tr[p].l=l,tr[p].r=r,pv=calc(l,r),pushup(p);
        }
        inline LL find(Re p,Re K){
            if(K<=tr[pl].size)return find(pl,K);
            return K<=tr[pl].size+1?p:find(pr,K-tr[pl].size-1);
        }
        inline LL split(Re L,Re R){//在Spaly维护的序列上获取区间[L,R]
            Re p=find(root,L-1),q=find(root,R+1);
            splay(p,0),splay(q,p);return tr[q].ps[0];
        }
        inline void insert(Re st,Re l,Re r){//在Spaly维护的序列上第st个点后插入节点{l,r,calc(l,r)}
            Re rt=0;build(rt,l,r);
            Re p=find(root,st),q=find(root,st+1);
            splay(p,0),splay(q,p);
            connect(rt,q,0),pushup(q),pushup(p);
        }
        inline void erase(Re pos){//删除Spaly维护的序列上第pos个节点
            Re p=split(pos,pos),fa=pf;
            tr[fa].ps[0]=pf=0,D.push(p),CL(p);
            pushup(fa),pushup(tr[fa].fa);
        }
        inline LL ask(Re L,Re R){return L<=R?tr[split(L,R)].S:0;}
        inline LL getpos(Re p,Re x){//寻找x所在连续段在Splay上的编号,如果找不到就返回其后面第一个连续段
            if(x<tr[p].l)return x<=tr[pl].rr?getpos(pl,x):p;
            return (x>=tr[p].l&&x<=tr[p].r)?p:getpos(pr,x);
        }
    }T1;
    struct QWQ{LL l,r,p,pos;};
    inline QWQ get(Re x){
        QWQ a;a.p=T1.getpos(T1.root,x),T1.splay(a.p,0);
        a.l=T1.tr[a.p].l,a.r=T1.tr[a.p].r,a.pos=T1.tr[T1.tr[a.p].ps[0]].size+1;
        return a;
    }
    inline void add(Re x){//加入点x,将一个连续段剖成两半
        QWQ a=get(x);
        T1.erase(a.pos),--a.pos;
        if(a.l<x)T1.insert(a.pos,a.l,x-1),++a.pos;
        if(x<a.r)T1.insert(a.pos,x+1,a.r);
    }
    inline void updata(QWQ &a){
        a.p=T1.find(T1.root,a.pos),a.l=T1.tr[a.p].l,a.r=T1.tr[a.p].r;
    }
    inline LL ask(Re L,Re R){//查询L,R
        if(T1.tr[T1.root].size==2)return 0;//没有连续段了
        QWQ bl=get(L),br=get(R);LL ans=0;
        if(R<br.l)--br.pos,updata(br);//如果返回了R后面第一个连续段,稍微处理下
        Re flag1=(L>=bl.l&&L<=bl.r),flag2=(R>=br.l&&R<=br.r);//flag1,flag2分别表示左右端点是否在连续段中间
        if(flag1&&flag2&&bl.pos==br.pos)return calc(L,R);//同一连续段
        if(flag1)ans+=calc(L,bl.r),++bl.pos,updata(bl);//左端点所在连续段被割断
        if(flag2)ans+=calc(br.l,R),--br.pos,updata(br);//右端点所在连续段被割断
        ans+=T1.ask(bl.pos,br.pos);//中间完全包含的连续段直接求和
        return ans;
    }
    int main(){
    //    freopen("123.txt","r",stdin);
        in(n),in(T);
        for(Re i=1;i<=n;++i)in(A[i]),S[i]=S[i-1]+A[i],S1[i]=S1[i-1]+S[i]*i,S2[i]=S2[i-1]+S[i],a[i].v=A[i],a[i].id=i;
        for(Re i=1;i<=T;++i)in(Q[i].l),in(Q[i].r),in(Q[i].v),Q[i].id=i;
        sort(a+1,a+n+1),sort(Q+1,Q+T+1);
        T1.build0(T1.root,1,n);
        for(Re i=1,j=0;i<=T;++i){
            while(j<n&&a[j+1].v>Q[i].v)add(a[++j].id);
            Ans[Q[i].id]=ask(Q[i].l,Q[i].r);
        }
        for(Re i=1;i<=T;++i)print(Ans[i]),putchar('
    ');
    }
    
  • 相关阅读:
    PHP 数据类型
    PHP SAPI
    PHP 基础架构
    PHP7的变化
    mysql 选择优化的数据类型需要注意的几点
    彻底删除在github上提交的文件
    php7 新特性
    php缓冲区 一些笔记
    设计模式 一些概念
    mysql性能优化其中一部分小结
  • 原文地址:https://www.cnblogs.com/Xing-Ling/p/13238547.html
Copyright © 2020-2023  润新知