[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到根.
- [jzoj]4216. 【NOIP2015模拟9.12】平方和(splay)
- 4216. 【NOIP2015模拟9.12】平方和
- jzoj4216. 【NOIP2015模拟9.12】平方和(splay+码量)
- [jzoj]4216. 【NOIP2015模拟9.12】平方和【线段树二分+码量】
- 【NOIP2015 模拟9.12】平方和
- 【NOIP2015模拟9.12】平方和
- jzoj4216. 【NOIP2015模拟9.12】平方和
- JZOJ 4294【NOIP2015模拟11.2】复制&粘贴2
- JZOJ 4295【NOIP2015模拟11.2】愉快的logo设计
- JZOJ.4300[NOIP2015模拟11.3]装饰大楼 解题报告
- JZOJ 4302【NOIP2015模拟11.3】IOIOI卡片占卜
- JZOJ.4301[NOIP2015模拟11.3]备用钥匙 解题报告
- JZOJ.4302[NOIP2015模拟11.3]IOIOI卡片占卜 解题报告
- JZOJ 4307. 【NOIP2015模拟11.3晚】喝喝喝
- JZOJ 4298. 【NOIP2015模拟11.2晚】我的天
- jzoj. 4298. 【NOIP2015模拟11.2晚】我的天
- jzoj 4298【NOIP2015模拟11.2晚】我的天
- 【JZOJ 4216】 平方和
- 1007. 素数对猜想 (20)-浙大PAT乙级真题java实现
- 思维方式才是你职业瓶颈期的根本原因
- 《ArcGIS API For Javascipt基础学习》博客推荐
- Java实现inputstream流的复制
- duilib中list拖动表头大小内容大小跟随变化的一个示例
- [jzoj]4216. 【NOIP2015模拟9.12】平方和(splay)
- 单片机的数字滤波设计
- leetcode编程记录19 #47 Permutations II
- 这是我的新博客
- 组合动画
- 使用 Python 进行分布式系统协调
- 坚持这7个工作习惯,帮你成为更专业的设计师
- 【日常】今天首件有一个出现了在5V电源下S11驻波波形不正确的情况
- 链表——两个有序链表序列的交集