平方和

题目大意

给出一个N个整数构成的序列,有M次操作,每次操作有一下三种:
①Insert Y X,在序列的第Y个数之前插入一个数X;
②Add L R X,对序列中第L个数到第R个数,每个数都加上X;
③Query L R,询问序列中第L个数到第R个数的平方和。
30%的数据满足N≤1,000,M≤1,000。
另外20%的数据满足N≤100,000,M≤100,000,且不存在Insert操作。
100%的数据满足N≤100,000,M≤100,000,且Add和Insert操作中|X|≤1000,|Ai|≤1000。

序列之王好

我们可以用splay来做这道题。
对于第一个操作,这是splay基本操作。
现在,为了支持第三个操作,我们维护size[i]表示以i为根的子树有多少元素,num[i]表示以i为根的子树元素和,sum[i]表示以i为根的子树元素平方和。
那么,这很容易维护。
为了支持第二个操作,我们用add[i]表示以i为根的子树每个元素都加上add[i]。则对于i而言,sum[i]需改为 sum[i]+add[i]2size[i]+2add[i]num[i]
num[i]改为 num[i]+add[i]size[i]
然后维护标记即可。

参考代码

#include<cstdio>
#include<cstring>
#include<stack>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100000*2+5,mo=7459;
stack <int> s;
int size[maxn],key[maxn],num[maxn],sum[maxn],add[maxn],father[maxn],tree[maxn][2];
int i,j,k,l,r,mid,t,n,m,tot,root,x,y;
char ch;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
char get(){
    char ch=getchar();
    while (ch<'A'||ch>'Z') ch=getchar();
    return ch;
}
int pd(int x){
    if (x==tree[father[x]][0]) return 0;else return 1;
}
void updata(int x){
    size[x]=size[tree[x][0]]+size[tree[x][1]]+1;
    sum[x]=((sum[tree[x][0]]+sum[tree[x][1]])%mo+key[x]*key[x]%mo)%mo;
    num[x]=((num[tree[x][0]]+num[tree[x][1]])%mo+key[x])%mo;
}
void rotate(int x){
    int y=father[x],z=pd(x);
    father[x]=father[y];
    if (father[y]) tree[father[y]][pd(y)]=x;
    tree[y][z]=tree[x][1-z];
    if (tree[x][1-z]) father[tree[x][1-z]]=y;
    tree[x][1-z]=y;
    father[y]=x;
    updata(y);
    updata(x);
}
void clear(int x){
    if (add[x]){
        if (tree[x][0]){
            key[tree[x][0]]=(key[tree[x][0]]+add[x])%mo;
            sum[tree[x][0]]=((sum[tree[x][0]]+add[x]*add[x]%mo*size[tree[x][0]])%mo+2*add[x]%mo*num[tree[x][0]]%mo)%mo;
            num[tree[x][0]]=(num[tree[x][0]]+add[x]*size[tree[x][0]]%mo)%mo;
            add[tree[x][0]]=(add[tree[x][0]]+add[x])%mo;
        }
        if (tree[x][1]){
            key[tree[x][1]]=(key[tree[x][1]]+add[x])%mo;
            sum[tree[x][1]]=((sum[tree[x][1]]+add[x]*add[x]%mo*size[tree[x][1]])%mo+2*add[x]%mo*num[tree[x][1]]%mo)%mo;
            num[tree[x][1]]=(num[tree[x][1]]+add[x]*size[tree[x][1]]%mo)%mo;
            add[tree[x][1]]=(add[tree[x][1]]+add[x])%mo;
        }
        add[x]=0;
    }
}
void remove(int x,int y){
    while (x!=y){
        s.push(x);
        x=father[x];
    }
    while (!s.empty()){
        clear(s.top());
        s.pop();
    }
}
void splay(int x,int y){
    remove(x,y);
    while (father[x]!=y){
        if (father[father[x]]!=y)
            if (pd(x)==pd(father[x])) rotate(father[x]);else rotate(x);
        rotate(x);
    }
}
int kth(int x,int y){
    if (size[tree[x][0]]+1==y) return x;
    else if (size[tree[x][0]]+1>y) return kth(tree[x][0],y);
    else return kth(tree[x][1],y-size[tree[x][0]]-1);
}
void split(int x,int y,int &l,int &r){
    if (!y){
        l=0;
        r=x;
    }
    else{
        int z=kth(x,y);
        splay(z,0);
        r=tree[z][1];
        father[r]=0;
        l=z;
        tree[l][1]=0;
        updata(l);
    }
}
void merge(int l,int r,int &x){
    if (!l) x=r;
    else if (!r) x=l;
    else{
        int z=kth(l,size[l]);
        splay(z,0);
        father[r]=z;
        tree[z][1]=r;
        x=z;
        updata(x);
    }
}
int main(){
    n=read();
    tot=n;
    fo(i,1,n){
        key[i]=read();
        key[i]=(key[i]%mo+mo)%mo;
        if (i>1){
            father[i-1]=i;
            tree[i][0]=i-1;
        }
        size[i]=i;
        updata(i);
    }
    root=n;
    splay(1,0);
    root=1;
    m=read();
    fo(i,1,m){
        ch=get();
        if (ch=='I'){
            y=read();x=read();
            x=(x%mo+mo)%mo;
            key[++tot]=x;
            updata(tot);
            split(root,y-1,l,r);
            merge(tot,r,r);
            merge(l,r,root);
        }
        if (ch=='A'){
            x=read();y=read();t=read();
            t=(t%mo+mo)%mo;
            split(root,y,l,r);
            split(l,x-1,l,mid);
            key[mid]=(key[mid]+t)%mo;
            sum[mid]=((sum[mid]+t*t%mo*size[mid])%mo+2*t%mo*num[mid]%mo)%mo;
            num[mid]=(num[mid]+t*size[mid]%mo)%mo;
            add[mid]=(add[mid]+t)%mo;
            merge(l,mid,l);
            merge(l,r,root);
        }
        if (ch=='Q'){
            x=read();y=read();
            split(root,y,l,r);
            split(l,x-1,l,mid);
            printf("%d\n",sum[mid]);
            merge(l,mid,l);
            merge(l,r,root);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值