[jzoj]4216. 【NOIP2015模拟9.12】平方和(splay)

来源:互联网 发布:各国进出口贸易数据 编辑:程序博客网 时间:2024/06/14 13:03

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

  • 记得很久以前搞过这道题,那时候用pascal码了7000+

  • 现在用splay,发现细节很多,自己很弱,打了很久.

  • 基本思路与《最大值》相似.

  • 关键在于如何下传标记,我的想法是:

void update(int x){    mup[x]=(mup[tr[x][0]]*mup[tr[x][1]]*key[x])%mo; //mup[]表示乘积    sum[x]=(sum[tr[x][0]]+sum[tr[x][1]]+key[x])%mo; //sum[]表示和    size[x]=size[tr[x][0]]+size[tr[x][1]]+1;//size[]表示子树大小    ans[x]=(ans[tr[x][0]]+ans[tr[x][1]]+key[x]*key[x])%mo; //ans[]表示答案}void push(int x,int y){    key[x]=(key[x]+y)%mo;    ans[x]=(ans[x]+sqr(y)*size[x]+2*sum[x]*y)%mo;    sum[x]=(sum[x]+size[x]*y)%mo;}void lazy(int x,int y){    if (y==0) return;    add[y]=(add[y]+add[x])%mo;}void get(int x)  //核心操作在于这里,我们先把x的节点给处理好,再更新父亲,然后再传给儿子.{    push(x,add[x]);    update(fa[x]);    lazy(x,tr[x][0]),lazy(x,tr[x][1]);    add[x]=0;}
  • rotate的时候也需要注意,需先传父亲,再自己传,再让儿子传(因为有可能一个点没有被传就被询问了)

  • 我们必须保证一个节点只要get之后它一定是当前答案,而标记所在的点一定不可能被询问.

  • rotate:

void rotate(int x){    if (x==0)return;    int y=fa[x],k=son(x);    if (add[y]) get(y);    if (add[x]) get(x);    if (fa[y]) tr[fa[y]][son(y)]=x;    if (tr[x][!k]) fa[tr[x][!k]]=y;    tr[y][k]=tr[x][!k], tr[x][!k]=y, fa[x]=fa[y], fa[y]=x;    if (add[tr[x][k]]) get(tr[x][0]);    if (add[tr[y][0]]) get(tr[y][0]);    if (add[tr[y][1]]) get(tr[y][1]);    update(y),update(x);}
  • 但很明显这种方法又笨由丑.

  • 我再想了想打区间修改时是如何搞得,于是就有了下面的代码:

#include <iostream>#include <cstring>#include <cstdio>#define fo(i,a,b) for(i=a;i<=b;i++)#define N 200010#define M 20#define mo 7459using namespace std;int n,m,i,x,y,z,lr,rl,rt,sz,tr[N][2],fa[N];int sum[N],key[N],ans[N],add[N],d[N],size[N];char ch[M];int son(int x){ return tr[fa[x]][1]==x;}int sqr(int x){ return (x*x)%mo;}void update(int x){    size[x]=size[tr[x][0]]+size[tr[x][1]]+1;    sum[x]=(sum[tr[x][0]]+sum[tr[x][1]]+key[x])%mo;    ans[x]=(ans[tr[x][0]]+ans[tr[x][1]]+sqr(key[x]))%mo;}void back(int x,int y){    if (y==0) return;    ans[x]=(ans[x]+sqr(y)*size[x]+2*sum[x]*y)%mo;    sum[x]=(sum[x]+size[x]*y)%mo;    add[x]=(add[x]+y)%mo;    key[x]=(key[x]+y)%mo;}void clear(int x){    if (tr[x][0]) back(tr[x][0],add[x]);    if (tr[x][1]) back(tr[x][1],add[x]);    add[x]=0;}void rotate(int x){    if (x==0) return;    int y=fa[x],k=son(x);    if (fa[y]) tr[fa[y]][son(y)]=x;    if (tr[x][!k]) fa[tr[x][!k]]=y;    tr[y][k]=tr[x][!k], tr[x][!k]=y, fa[x]=fa[y], fa[y]=x;    update(y),update(x);}void remove(int x,int y){    while (x!=y) d[++d[0]]=x, x=fa[x];    while (d[0]) clear(d[d[0]--]);}void splay(int x,int y){    remove(x,y);    for(int f;(f=fa[x])&&(fa[x]!=y);rotate(x))        rotate((fa[f]!=y)?((son(f)==son(x))?f:x):0);}int pos(int num){    int nw=rt, rank=0;    while (1)    {        if (tr[nw][0] && rank+size[tr[nw][0]]+1>num) nw=tr[nw][0]; else        {            rank+=size[tr[nw][0]]+1;            if (rank==num) return nw;            nw=tr[nw][1];        }    }} void ins(int y,int x){    int nw=pos(y);    splay(nw,0),rt=nw, fa[++sz]=rt, key[sz]=x, tr[sz][0]=tr[rt][0], tr[rt][0]=sz, fa[tr[sz][0]]=sz, update(sz);    splay(sz,0),rt=sz;}int main(){    scanf("%d",&n), size[1]=1;    fo(i,2,n+1)        scanf("%d",&key[i]),tr[i][0]=i-1,fa[i-1]=i,update(i);    tr[n+2][0]=n+1, fa[n+1]=sz=rt=n+2, update(n+2);    for(scanf("%d",&m);m;m--)    {        scanf("%s%d%d",ch+1,&x,&y);        if (ch[1]=='A') scanf("%d",&z);        if (ch[1]!='I') lr=pos(x), rl=pos(y+2), splay(lr,0),splay(rl,lr), rt=lr;        switch(ch[1])        {            case 'I':ins(x+1,y); break;            case 'A':back(tr[rl][0],z), rt=tr[rl][0], splay(tr[rl][0],0); break;            case 'Q':printf("%d\n",(ans[tr[rl][0]]+mo*mo)%mo); break;        }    }}
  • 这个代码比上面几乎要快了0.5s.

  • 可见常数小了多少..

  • 但也要注意,每次更新一个点时,一定要记得splay到根.

阅读全文
0 0
原创粉丝点击