【NOIP2015模拟9.12】平方和

7 篇文章 0 订阅

Description

给出一个N个整数构成的序列,有M次操作,每次操作有一下三种:
①Insert Y X,在序列的第Y个数之前插入一个数X;
②Add L R X,对序列中第L个数到第R个数,每个数都加上X;
③Query L R,询问序列中第L个数到第R个数的平方和。

Input

第一行一个正整数N,表示初始序列长度。
第二行N个整数Ai,表示初始序列中的数。
第三行一个正整数M,表示操作数。
接下来M行,每行一种操作。

Output

对于每一个Query操作输出答案。由于答案可能很大,请mod 7459后输出。

Sample Input

5
1 2 3 4 5
5
Query 1 3
Insert 2 5
Query 2 4
Add 5 6 7
Query 1 6

Sample Output

14
38
304
样例解释:
第二次操作后的序列:1,5,2,3,4,5。
第四次操作后的序列:1,5,2,3,11,12。

Data Constraint

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。

Solution

这题可以用离线线段树的方法来做,但是比较复杂,码量较大
我用的是splay做
维护平方和只需要维护和,大小还有平方和就行了
因为 (a+x)2=a2+2ax+x2
第一项通过维护原来的平方和
第二项通过维护和
第三项就是lazy

Code

#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 201000
#define mo 7459
#define ll long long
using namespace std;
int t[N][2],fa[N],lz[N],tot,root,s[N],n;
ll size[N],sum[N],d[N],ssum[N];
void update(int x)
{
    size[x]=size[t[x][0]]+size[t[x][1]]+1;
    sum[x]=(sum[t[x][0]]+sum[t[x][1]]+d[x])%mo;
    ssum[x]=(ssum[t[x][0]]+ssum[t[x][1]]+d[x]*d[x])%mo;
}
void down(int x,ll y)
{
    ssum[x]=(ssum[x]+sum[x]*y*2ll+y*y*size[x])%mo;
    sum[x]=(sum[x]+y*size[x])%mo;
    d[x]=(d[x]+y+mo)%mo;lz[x]=(lz[x]+y+mo)%mo;
}
void xc(int x)
{
    for(;x;x=fa[x]) s[++s[0]]=x;
    for(;s[0];s[0]--)
    {
        int y=s[s[0]];
        if(lz[y]!=0)
        {
            down(t[y][0],lz[y]);down(t[y][1],lz[y]);
            lz[y]=0;
        }
    }
}
int lr(int x){return x==t[fa[x]][1];}
void rotate(int x)
{
    int y=fa[x],k=lr(x);
    t[y][k]=t[x][1-k];if(t[x][1-k]) fa[t[x][1-k]]=y;
    fa[x]=fa[y];if(fa[y]) t[fa[y]][lr(y)]=x;
    t[x][1-k]=y;fa[y]=x;
    update(y);update(x);
}
void splay(int x,int y)
{
    xc(x);
    while(fa[x]!=y)
    {
        if(fa[fa[x]]!=y)
            if(lr(x)==lr(fa[x])) rotate(fa[x]);
            else rotate(x);
        rotate(x);
    }
    if(y==0) root=x;
}
int kth(int r,int x)
{
    if(size[t[r][0]]+1==x) return r;
    if(size[t[r][0]]>=x) return kth(t[r][0],x);
    else return kth(t[r][1],x-size[t[r][0]]-1);
}
int main()
{
    scanf("%d",&n);
    fa[1]=2;t[2][0]=1;update(1);
    fo(i,1,n)
    {
        int x;scanf("%d",&x);
        d[i+1]=(x+mo)%mo;fa[i+1]=i+2;t[i+2][0]=i+1;update(i+1);
    }
    root=n+2;update(n+2);tot=n+2;
    int ac;scanf("%d\n",&ac);
    for(;ac;ac--)
    {
        char ch;scanf("%c",&ch);
        if(ch=='Q')
        {
            int x,y;scanf("uery %d %d\n",&x,&y);
            x=kth(root,x);y=kth(root,y+2);
            splay(x,0);splay(y,x);
            printf("%d\n",ssum[t[y][0]]);
        }
        if(ch=='I')
        {
            int x,y,z;scanf("nsert %d %d\n",&x,&z);
            z=(z+mo)%mo;
            y=kth(root,x+1);x=kth(root,x);
            splay(x,0);splay(y,x);
            t[y][0]=++tot;fa[tot]=y;d[tot]=z;
            update(tot);update(y);update(x);
        }
        if(ch=='A')
        {
            int x,y,z;scanf("dd %d %d %d\n",&x,&y,&z);
            x=kth(root,x);y=kth(root,y+2);
            splay(x,0);
            splay(y,x);
            down(t[y][0],z);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值