好题线段树区间合并(CDQ分治)——cf1316F

Posted zsben991126

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了好题线段树区间合并(CDQ分治)——cf1316F相关的知识,希望对你有一定的参考价值。

/*
离线读入所有值后离散化 
题意要求的是,sum{ ai*2^(i-1)*aj*2^(n-j) }
 
分析这个式子,考虑进行分治
    区间[l,r]的 sum{ ai*2^(i-l)*aj*2^(r-j) }
    可以将式子分成 ai*2^(i-l) 和 aj*2^(r-j)两部分看,
    线段树维护四个值,sum,pre,suf,size:分别表示这个
        区间的答案,
        ai*2^(i-l)的和
        aj*2^(r-j)的和
        区间有效元素个数
    那么当两个区间合并时
        sum = suml*2^sizer + sumr*2^sizel + prel*sufr,对应i,j都从左区间里出,i,j都从右区间里出,i,j从左右区间出 
        pre = prel + prer*2^sizel,对应左区间的pre,右区间的pre*左区间的元素个数 
        suf = sufr + sufl*2^sizer
        size = sizel + sizer 
初始值:sum=0,size=0,pre=0,suf=0
考虑位置i的值变为x 
     下降到叶子结点:size=1,pre=suf=x 
*/
#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define mod 1000000007
#define N 600005
 
ll Pow(ll a,ll b){
    ll res=1;
    while(b){
        if(b%2)res=res*a%mod;
        b>>=1;a=a*a%mod;
    }
    return res;
}
ll P[N];
 
struct Node{
    int id,x;
}q[N<<1];
ll n,p[N],Q; 
 
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
struct Seg{
    ll sum,pre,suf,size;
}s[N<<2];
Seg merge(Seg A,Seg B){
    Seg res;
    res.sum=(A.sum*P[B.size]%mod+B.sum*P[A.size]%mod+A.pre*B.suf%mod)%mod;
    res.pre=(A.pre+B.pre*P[A.size]%mod)%mod;
    res.suf=(B.suf+A.suf*P[B.size]%mod)%mod;
    res.size=(A.size+B.size)%mod;
    return res;
}
void update(int pos,ll x,int l,int r,int rt){
    if(l==r){
        s[rt].sum=0;
        s[rt].pre=s[rt].suf=x;
        if(x!=0)s[rt].size=1;
        else s[rt].size=0;
        return;
    }
    int m=l+r>>1;
    if(pos<=m)update(pos,x,lson);
    else update(pos,x,rson);
    s[rt]=merge(s[rt<<1],s[rt<<1|1]);
}
 
int pos1[N],pos2[N],m;
struct OP{
    int id,x;
}op[N<<1];
int cmp(OP a,OP b){return a.x<b.x;}
 
int main(){
    P[0]=1;
    for(int i=1;i<=300000;i++)P[i]=P[i-1]*2ll%mod;
    
    scanf("%lld",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld",&p[i]);
        op[i].id=i;op[i].x=p[i];
    }
    scanf("%lld",&Q);
    for(int i=1;i<=Q;i++){
        scanf("%lld%lld",&q[i].id,&q[i].x);
        op[i+n].id=i+n;
        op[i+n].x=q[i].x;
    }
    m=n+Q;
    
    sort(op+1,op+1+n+Q,cmp);
    for(int i=1;i<=n+Q;i++){
        if(op[i].id<=n)
            pos1[op[i].id]=i;
        else pos2[op[i].id-n]=i;
    }
    
    memset(s,0,sizeof s);
    for(int i=1;i<=n;i++)
        update(pos1[i],p[i],1,m,1);
    
    ll base=Pow(P[n],mod-2);
    printf("%lld
",s[1].sum*base%mod);
    for(int i=1;i<=Q;i++){
        update(pos1[q[i].id],0,1,m,1);
        update(pos2[i],q[i].x,1,m,1);
        cout<<s[1].sum*base%mod<<
;    
        pos1[q[i].id]=pos2[i];    
    }
}

 

以上是关于好题线段树区间合并(CDQ分治)——cf1316F的主要内容,如果未能解决你的问题,请参考以下文章

[用CDQ分治解决区间加&区间求和]习作

线段树逆序对(偏序)——cf1187D好题!

线段树分治总结(线段树分治,线段树,并查集,树的dfn序,二分图染色)

Codeforces 1140F 线段树 分治 并查集

bzoj4631踩气球 线段树

线段树维护区间合并——cf1285E