【JZOJ 4216】 平方和

8 篇文章 0 订阅

Description

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

Analysis

插入的话,splay好做,平方和什么的区间维护好做
size表示区间点数,sum表示区间和,sum2表示区间平方和,add表示tag,key表示该点键值
这题因为有区间修改所以要打lazy tag
实现细节:

  1. 开longlong
  2. 因为头尾各有一个空结点,所以区间修改的时候注意:空结点的sum,size,sum2都要修改,但是key永远为0,修改的时候要特判,不能动。
  3. 建议将提取区间写成一个函数,一次性写好,要不然用size[a[x][0]]+1的时候如果忘记splay到根就会跪(之前老是忘)
  4. 开完虚拟结点n+2后,要记得将表示结点数量的变量n赋为n+2

Code

#include<cstdio>
#include<stack>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int N=200010,mo=7459;
int n,root,size[N],add[N],f[N],a[N][2];
ll key[N],sum[N],sum2[N];
stack<int> q;
int pd(int x)
{
    return x==a[f[x]][1];
}
int update(int x)
{
    size[x]=1+size[a[x][0]]+size[a[x][1]];
    sum[x]=key[x]+sum[a[x][0]]+sum[a[x][1]];
    sum2[x]=((key[x]*key[x]%mo+sum2[a[x][0]]+sum2[a[x][1]])%mo+mo)%mo;
}
void change(int x,ll z)
{
    if(!x) return;
    sum2[x]=((sum2[x]+z*z*size[x]%mo+2*sum[x]*z%mo)%mo+mo)%mo,sum[x]+=size[x]*z,add[x]+=z;
    if(x<=n) key[x]+=z;
}
void down(int x)
{
    if(!add[x]) return;
    change(a[x][0],add[x]);
    change(a[x][1],add[x]);
    add[x]=0;
}
void remove(int x,int y)
{
    for(;x!=y;x=f[x]) q.push(x);
    while(!q.empty())
    {
        down(q.top());q.pop();
    }
}
void rotate(int x)
{
    int y=f[x],z=pd(x);
    a[y][z]=a[x][1-z];
    if(a[x][1-z]) f[a[x][1-z]]=y;
    f[x]=f[y];
    if(f[y]) a[f[y]][pd(y)]=x;
    f[y]=x,a[x][1-z]=y;
    update(y);
}
void splay(int x,int y)
{
    if(!y) root=x;
    remove(x,y);
    while(f[x]!=y)
    {
        if(f[f[x]]!=y)
            if(pd(x)==pd(f[x])) rotate(f[x]);
            else rotate(x);
        rotate(x);
    }
    update(x);
}
int kth(int x,int k)
{
    if(size[a[x][0]]+1==k) return x;
    if(k>size[a[x][0]]) return kth(a[x][1],k-size[a[x][0]]-1);
    else return kth(a[x][0],k);
}
int split(int x,int y)
{
    x=kth(root,x),y=kth(root,y+2);
    splay(x,0),splay(y,x);
    return a[y][0];
}
int main()
{
    int _,x,y,z;
    char ch;
    scanf("%d",&n);
    fo(i,1,n) scanf("%lld",&key[i]);
    f[1]=n+1,key[n+1]=0,a[n+1][1]=1;
    fo(i,2,n) f[i]=i-1,a[i-1][1]=i;
    f[n+2]=n,key[n+2]=0,a[n][1]=n+2,size[n+2]=1;
    splay(n+2,0);
    n=n+2;
    for(scanf("%d\n",&_);_;_--)
    {
        scanf("%c",&ch);
        if(ch=='Q')
        {
            scanf("uery %d %d\n",&x,&y);
            x=split(x,y);
            printf("%lld\n",sum2[x]);
        }
        if(ch=='I')
        {
            scanf("nsert %d %d\n",&y,&z);
            x=kth(root,y);
            y=kth(root,y+1);
            splay(x,0),splay(y,x);
            a[y][0]=++n,f[n]=y,key[n]=z;
            splay(n,0);
        }
        if(ch=='A')
        {
            scanf("dd %d %d %d\n",&x,&y,&z);
            x=split(x,y);
            change(x,z);
        }
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值