JZOJ4216 平方和

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。


题目大意:

给一个序列,每次可以区间加上一个数,还可以插入一个数,动态求某区间的平方和。

题解:

如果没有插入操作,相信只要对数学有点造诣的人,都会用线段树做。

把一个数a增加b,它的平方和会变成 (a+b)2a2=2ab+b2
把一个区间加上b,那么我们只需要维护这个区间的个数,和,平方和,就可以求出新的平方和。

用splay维护也是一样的,插入时需要插入到第c个数旁,那么就在splay里找到一个点x,它和它的子树的大小是c-1,把x旋到根,再找到一个点y,它和它的子树的大小是c,把y旋到根的右边,那么在y的左节点新建一个点,就是要插入的点,再把它旋到根,至此就完成了。

Code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define ll long long
#define mo 7459
using namespace std;

const int Maxn = 200100;
int n, m, fa[Maxn], t[Maxn][2], d[Maxn], td;
struct Point {
    ll z, siz, sum, sqr, lz;
}a[Maxn];

void update(int x) {
    a[x].siz = a[t[x][0]].siz + a[t[x][1]].siz + 1;
    a[x].sum = a[t[x][0]].sum + a[t[x][1]].sum + a[x].z;
    a[x].sqr = a[t[x][0]].sqr + a[t[x][1]].sqr + a[x].z * a[x].z;
}
int lr(int x) {return t[fa[x]][1] == x;}
void chan(int x, ll y) {
    if(!x) return;
    a[x].sqr += 2 * a[x].sum * y + a[x].siz * y * y;
    a[x].sum += a[x].siz * y;
    a[x].z += y;
    a[x].lz += y;
}
void down(int x) {if(a[x].lz) chan(t[x][0], a[x].lz), chan(t[x][1], a[x].lz), a[x].lz = 0;}
void rotate(int x) {
    int y = fa[x], k = lr(x);
    t[y][k] = t[x][!k];
    if(t[x][!k]) fa[t[x][!k]] = y;
    fa[x] = fa[y];
    if(fa[y]) t[fa[y]][lr(y)] = x; 
    t[x][!k] = y; fa[y] = x;
    update(y); update(x);
}
void xc(int x) {
    while(x) d[++ d[0]] = x, x = fa[x];
    for(; d[0]; d[0] --) down(d[d[0]]);
}
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);
    }
}
int find(int x, int y) {
    down(x);
    if(a[t[x][0]].siz >= y) return find(t[x][0], y);
    if(a[t[x][0]].siz + 1 == y) return x;
    return find(t[x][1], y - a[t[x][0]].siz - 1);
}


void Init() {
    int root;
    scanf("%d", &n);
    a[1].siz = 1;
    fo(i, 1, n) {
        scanf("%lld", &a[i + 1].z);
        fa[i] = i + 1; t[i + 1][0] = i; update(i + 1);
    }
    fa[n + 1] = n + 2; t[n + 2][0] = n + 1; update(n + 2);
    root = n + 2; td = n + 2;

    scanf("%d", &m);
    fo(i, 1, m) {
        char s[10]; int x, y, z, l, r;
        scanf("%s", s); scanf("%d %d", &x, &y);
        if(s[0] == 'Q') {
            l = find(root, x); r = find(root, y + 2);
            splay(l, 0); splay(r, l); root = l;
            printf("%lld\n", a[t[r][0]].sqr % mo);  
        } 
        if(s[0] == 'I') {
            l = find(root, x); r = find(root, x + 1);
            splay(l, 0); splay(r, l);
            fa[++ td] = r;  a[td].siz = 1; a[td].z = y; a[td].sum = y; a[td].sqr = (ll)y * y;
            t[r][0] = td;
            splay(td, 0); root = td;
        }
        if(s[0] == 'A') {
            scanf("%d", &z);
            l = find(root, x); r = find(root, y + 2);
            splay(l, 0); splay(r, l);
            chan(t[r][0], z);
            root = t[r][0];
            splay(t[r][0], 0); 
        }
    }
}

int main() {
    freopen("4216.in", "r", stdin);
    freopen("4216.out", "w", stdout);
    Init();
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值