描述

小Ho:好麻烦啊~~~~~

小Hi:小Ho你在干嘛呢?

小Ho:我在干活啊!前几天老师让我帮忙管理一下团队的人员,但是感觉好难啊。

小Hi:说来听听?

小Ho:事情是这样的。我们有一个运动同好会,每天都有人加入或者退出,所以老师让我帮忙管理一下人员。每个成员有一个互不相同的id和他对我们同好会的兴趣值val,每隔一段时间一些成员的兴趣值就会发生变化。老师有时候也会问我一些成员的兴趣值。

小Hi:所以你就需要一个表格来管理信息咯?

小Ho:是啊,但是我们同好会的成员实在是太多了!我感觉完全搞不定啊。

小Hi:这样啊,那不如让我来帮帮你吧!

小Ho:真的吗?

小Hi:当然是真的啦,小Ho,你先告诉我有多少种需要完成的事情?

小Ho:一共有4种情况:

1. 加入:一个新的成员加入同好会,我会分配给他一个没有使用的id,并且询问他的兴趣值val。

2. 修改:id在区间[a,b]内的成员,兴趣值同时改变k,k有可能是负数,表示他们失去了对同好会的兴趣。

3. 退出:id在区间[a,b]内的成员要退出同好会,虽说是区间,也有可能只有1个人。

4. 询问:老师会问我在区间[a,b]内的成员总的兴趣值。

小Hi:我明白了,让我想一想该如何解决。

提示:Splay

输入

第1行:1个正整数n,表示操作数量,100≤n≤200,000

第2..n+1行:可能包含下面4种规则:

1个字母'I',紧接着2个数字id,val,表示一个编号为id的新成员加入,其兴趣值为val,1≤id≤100,000,000,1≤val≤10,000,000,保证在团队中的每个人id都不相同。

1个字母'Q',紧接着2个数字a,b。表示询问团队中id在区间[a,b]的所有成员总兴趣值,保证区间内至少有一个成员,结果有可能超过int的范围。

1个字母'M',紧接着3个数字a,b,d,表示将团队中id在区间[a,b]的成员兴趣值都改变d,其中d有可能为负数。保证操作之后每个成员的兴趣值仍然在0~10,000,000。

1个字母'D',紧接着2个数字a,b,表示将团队中id在区间[a,b]的成员除去。

注意有可能出现一个id为1的成员加入团队,被除去之后,又有一个新的id为1的成员加入团队的情况。

输出

若干行:每行1个整数,表示针对询问的回答,保证一定有合法的解

样例输入

9
I 1 1
I 2 2
I 3 3
Q 1 3
M 1 2 2
Q 1 3
D 2 3
I 4 2
Q 1 4

样例输出

6
10
5

一代代码:按照hihocoder上面的模板打的:

ps:

如果没有新加入节点这一操作,就是线段树+laxy操作了......

有新加入节点,splay+laxy......

似乎明白了什么......

正题:

和上一个代码比较,加了id,val,tot,lazy等变量,和一些函数。

注意:查询,修改,和删除函数依旧没有加边界两点。。。

因为此处的做法是找到最大的x<a,最小的y>b(不能等于!),然后查询,修改或者删去x,y之间的(不包括x和y),这样保证处理的区间一定在[a,b]上。

 (insert如果用地址符的话,可以不单独处理root==0的情况,这里懒得改了,见下面几道题的写法。)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
const int maxN=;
const ll inf=0x3f3f3f3f;
struct SplayData
{
int fa,ch[],id,val,num;
ll totVal,lazy;
SplayData()//自动初始每一个新建data
{
totVal=val=lazy=;
ch[]=ch[]=;
num=fa=;
}
};
struct SplayTree
{
int cnt;
int root;
SplayData S[maxN];
SplayTree()//自动初始树
{
root=;
cnt=;
Insert(-inf,);//插入极大极小值
Insert(inf,);
}
void Find(int x)
{
if (root==) return ;//树为空,肯定找不到
int now=root;
while ((S[now].ch[x>S[now].id]!=)&&(x!=S[now].id)) {
now=S[now].ch[x>S[now].id];
}
Splay(now,);
}
void marking(int x,int delta)
{
S[x].lazy+=delta;
S[x].totVal+=S[x].num*delta;
S[x].val+=delta;
}
void update(int x)//对x
{
S[x].num=;
S[x].totVal=S[x].val;
if(S[x].ch[]){
S[x].num+=S[S[x].ch[]].num;
S[x].totVal+=S[S[x].ch[]].totVal;
}
if(S[x].ch[]){
S[x].num+=S[S[x].ch[]].num;
S[x].totVal+=S[S[x].ch[]].totVal;
}
}
void pushdown(int x)//对x的儿子
{
if(S[x].ch[]) marking(S[x].ch[],S[x].lazy);
if(S[x].ch[]) marking(S[x].ch[],S[x].lazy);
S[x].lazy=;
update(x);
}
void Insert(int id,int val)
{
if(root==){
root=++cnt;
S[root].id=id;
S[root].val=val;
S[root].totVal=val;
}
else {
int x=bst_insert(root,id,val);
if(x) Splay(x,);
}
}
int bst_insert(int now,int id,int val)
{
pushdown(now);
int tmp=;
if(S[now].id>id){
if(S[now].ch[]) tmp=bst_insert(S[now].ch[],id,val);
else {
S[++cnt].id=id;
S[cnt].val=val;
S[cnt].totVal=val;
S[now].ch[]=cnt;
S[cnt].fa=now;
tmp=cnt;
}
}
else if(S[now].id<id){
if(S[now].ch[]) tmp=bst_insert(S[now].ch[],id,val);
else {
S[++cnt].id=id;
S[cnt].val=val;
S[cnt].totVal=val;
S[now].ch[]=cnt;
S[cnt].fa=now;
tmp=cnt;
}
}
update(now);
return tmp;
}
void Rotate(int x)
{
int y=S[x].fa;
int z=S[y].fa;
pushdown(y);
pushdown(x);
int k1=S[y].ch[]==x;
int k2=S[z].ch[]==y;
S[z].ch[k2]=x;
S[x].fa=z;
S[y].ch[k1]=S[x].ch[k1^];
S[S[x].ch[k1^]].fa=y;
S[x].ch[k1^]=y;
S[y].fa=x;
update(y);
update(x);
return;
}
void Splay(const int x,int goal)
{
if(!x) return ;//!
while (S[x].fa!=goal)
{
int y=S[x].fa;
int z=S[y].fa;
if (z!=goal)
((S[z].ch[]==y)^(S[y].ch[]==x))?Rotate(x):Rotate(y);//异则x,同则y
Rotate(x);
}
if (goal==)
root=x;
return;
}
int Next(int x,int opt)
{
Find(x);//先移‘x’到根
int now=root;
if ((S[now].id<x)&&(opt==)) return now; //对根做处理,暂时没有发现这种情况。
if ((S[now].id>x)&&(opt==)) return now;
now=S[now].ch[opt];
while (S[now].ch[opt^]!=) now=S[now].ch[opt^];//沿子树一直找
return now;
}
void DeleteRange(int l,int r)
{
Insert(l,);//防止没有边界
Insert(r,);
int prep=Next(l,);//移到根
int nex=Next(r,);//移到根的右儿子
Splay(prep,);
Splay(nex,prep);
S[nex].ch[]=;//删去根的右儿子的左儿子
update(nex);
update(prep);
}
ll Query(int l,int r)//这两个没有删,不能加边界。
{
int prep=Next(l,);//移到根
int nex=Next(r,);//移到根的右儿子
Splay(prep,);
Splay(nex,prep);
return S[S[nex].ch[]].totVal;//...
}
int Modify(int l,int r,int delta)
{
int prep=Next(l,);
int nex=Next(r,);
Splay(prep,);
Splay(nex,prep);
marking(S[nex].ch[],delta);
}
};
SplayTree SP;
int main()
{
int k,l,r,n,id,val,a,b;
scanf("%d",&n);
for (int i=;i<=n;i++){
char opt[];
scanf("%s",opt);
if (opt[]=='I'){
scanf("%d%d",&id,&val);
SP.Insert(id,val);
}
else if (opt[]=='Q'){
scanf("%d%d",&a,&b);
printf("%lld\n",SP.Query(a,b));
}
else if (opt[]=='M'){
scanf("%d%d%d",&a,&b,&val);
SP.Modify(a,b,val);
}
else {
scanf("%d%d",&a,&b);
SP.DeleteRange(a,b);
}
}
return ;
}

----------------------------------------------------2018.1.2更新----------------------------------------------

由于之前splay每这么深入,现在从新打板子。

二代代码:快了很多。

#include<iostream>
#include<cstdio>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long ll;
#define maxn 200010
struct SplayTree
{
int son[maxn][],fa[maxn],key[maxn],siz[maxn];
ll val[maxn],lazy[maxn],sum[maxn];
int cnt,root,n;
void Pushup(int rt)
{
int ls=son[rt][],rs=son[rt][];
siz[rt]=,sum[rt]=val[rt];
if(ls) siz[rt]+=siz[ls],sum[rt]+=sum[ls];
if(rs) siz[rt]+=siz[rs],sum[rt]+=sum[rs];
}
void Pushdown(int rt)
{
if(lazy[rt]){
int ls=son[rt][],rs=son[rt][];
if(ls)lazy[ls]+=lazy[rt],sum[ls]+=siz[ls]*lazy[rt],val[ls]+=lazy[rt];
if(rs)lazy[rs]+=lazy[rt],sum[rs]+=siz[rs]*lazy[rt],val[rs]+=lazy[rt];
lazy[rt]=;
}
}
int New_nod(int f,int k,int v)
{
son[++cnt][]=son[cnt][]=;
fa[cnt]=f; siz[cnt]=;
key[cnt]=k; val[cnt]=v;
sum[cnt]=v; return cnt;
}
void init()
{
cnt=;
root=New_nod(,-INF,);//先加了2个边界点
son[root][]=New_nod(root,INF,);
Pushup(root);
}
void Rotate(int x,int c)//c==0代表右旋
{
int f=fa[x],nxtf=fa[f];
Pushdown(f); Pushdown(x);
if(nxtf!=) son[nxtf][son[nxtf][]!=f]=x;
fa[x]=nxtf;
son[f][c]=son[x][!c]; fa[son[x][!c]]=f;
son[x][!c]=f; fa[f]=x;
Pushup(f);//Pushup(x);这里没有更新是因为Splay函数最后要更新x。
}
void Splay(int x,int y)//把x旋转为y的子节点,首先要确保y是x的祖先
{
Pushdown(x);
while(fa[x]!=y){
int f=fa[x];
int c1=(son[fa[f]][]==f),c2=(son[f][]==x);
if(fa[f]==y) Rotate(x,!c2);
else{
if(c1^c2) Rotate(x,!c2),Rotate(x,c2);//相异时两次提高x
else Rotate(f,!c1),Rotate(x,!c1);// 相异时依此提高f,x
}
} Pushup(x); if(!y) root=x;
}
int Insert(int x,int val)
{
int now=root,f;
while(now!=){
Pushdown(now);
siz[now]++; sum[now]+=val;
f=now; now=son[now][x>=key[now]];
}
int id=New_nod(f,x,val);
son[f][x>=key[f]]=id;
Splay(id,); return id;
}
int Find(int x)
{
int now=root;
while(now){
Pushdown(now);
if(key[now]==x)return now;
now=son[now][x>=key[now]];
} return now;
}
int Findpre(int x)//查询严格小于x的节点,可以和下面的函数合并。
{
int now=root,ans;
while(now){
Pushdown(now);
if(key[now]<x)ans=now,now=son[now][];
else now=son[now][];
}
return ans;
}
int Findnxt(int x)//查询严格大于x的节点,可以和上面的函数合并。
{
int now=root,ans;
while(now){
Pushdown(now);
if(key[now]>x)ans=now,now=son[now][];
else now=son[now][];
}
return ans;
}
void Delete(int l,int r)
{
int x=Findpre(l),y=Findnxt(r);
Splay(x,); Splay(y,x);
son[y][]=;//内存不够时写内存池
Pushup(y); Pushup(x);
}
ll Query(int l,int r)
{
int x=Findpre(l),y=Findnxt(r);
Splay(x,); Splay(y,x);
return sum[son[y][]];
}
void Update(int l,int r,int v)
{
int x=Findpre(l),y=Findnxt(r);
Splay(x,); Splay(y,x);
int id=son[y][];
sum[id]+=(ll)siz[id]*v;
val[id]+=v; lazy[id]+=v;
Pushup(y); Pushup(x);
}
} Tree;
int main()
{
Tree.init();scanf("%d",&Tree.n);
for(int i=;i<=Tree.n;i++){
char s[]; int x,y,d;
scanf("%s",s); scanf("%d%d",&x,&y);
if(s[]=='I') Tree.Insert(x,y);
else if(s[]=='Q') printf("%lld\n",Tree.Query(x,y));
else if(s[]=='D') Tree.Delete(x,y);
else scanf("%d",&d),Tree.Update(x,y,d);
}
return ;
}

HihoCoder1333 :平衡树(splay+lazy)(区间加值,区间删除)的更多相关文章

  1. 区间加值,区间gcd, 牛客949H

    牛客小白月赛16H 小阳的贝壳 题目链接 题意 维护一个数组,支持以下操作: 1: 区间加值 2: 询问区间相邻数差的绝对值的最大值 3: 询问区间gcd 题解 设原数组为\(a\), 用线段树维护\ ...

  2. HDU 3577 Fast Arrangement ( 线段树 成段更新 区间最值 区间最大覆盖次数 )

    线段树成段更新+区间最值. 注意某人的乘车区间是[a, b-1],因为他在b站就下车了. #include <cstdio> #include <cstring> #inclu ...

  3. cf834D(dp+线段树区间最值,区间更新)

    题目链接: http://codeforces.com/contest/834/problem/D 题意: 每个数字代表一种颜色, 一个区间的美丽度为其中颜色的种数, 给出一个有 n 个元素的数组, ...

  4. POJ 3468 A Simple Problem with Integers(线段树功能:区间加减区间求和)

    题目链接:http://poj.org/problem?id=3468 A Simple Problem with Integers Time Limit: 5000MS   Memory Limit ...

  5. HDU 5828 Rikka with Sequence(线段树区间加开根求和)

    Problem DescriptionAs we know, Rikka is poor at math. Yuta is worrying about this situation, so he g ...

  6. 【BZOJ】1798: [Ahoi2009]Seq 维护序列seq 线段树多标记(区间加+区间乘)

    [题意]给定序列,支持区间加和区间乘,查询区间和取模.n<=10^5. [算法]线段树 [题解]线段树多重标记要考虑标记与标记之间的相互影响. 对于sum*b+a,+c直接加上即可. *c后就是 ...

  7. 线段树_区间加乘(洛谷P3373模板)

    题目描述 如题,已知一个数列,你需要进行下面三种操作: 1.将某区间每一个数乘上x 2.将某区间每一个数加上x 3.求出某区间每一个数的和 输入格式: 第一行包含三个整数N.M.P,分别表示该数列数字 ...

  8. LOJ6283 数列分块入门 7 (分块 区间加/乘)题解

    题意:区间加,区间乘,单点询问 思路:假设一个点为a,那么他可以表示为m * a + sum,所以区间加就变为m * a + sum + sum2,区间乘变为m * m2 * a + sum * m2 ...

  9. HDU 1754区间最值 & SPLAY

    真是亲切的1754啊..第一道傻逼版的线段树做的是这个,后来学了zkw做的是这个,在后来决定打lrj线段树又打了一遍,如今再用splay和老朋友见面   从上到下依次为:加了读入优化的splay,sp ...

随机推荐

  1. js错误: Unexpected number in JSON at position 2792 value里面有双引号怎么解决

    源头  出现这个报错提示,大家从错误就可以看的出来,这就是json的错误,一般来说都是json格式出现了错误,本人遇到比较多的情况就是json字符串里面出现了一些会影响json格式的符号,这次出现这个 ...

  2. Jest — ElasticSearch Java 客户端

    1. 介绍 任何使用过Elasticsearch的人都知道,使用基于rest的搜索API构建查询可能是单调乏味且容易出错的. 在本教程中,我们将研究Jest,一个用于Elasticsearch的HTT ...

  3. linux下proc里关于磁盘性能的参数(转)

    我们在磁盘写操作持续繁忙的服务器上曾经碰到一个特殊的性能问题.每隔 30 秒,服务器就会遇到磁盘写活动高峰,导致请求处理延迟非常大(超过3秒).后来上网查了一下资料,通过调整内核参数,将写活动的高峰分 ...

  4. Micro Python:运行在微控制器上的Python

    Micro Python运行在微控制器上的Python.遵守MIT协议.由剑桥大学的理论物理学家乔治·达明设计.和Arduino类似,但Micro Python更强大. Micro Python的软件 ...

  5. ios何时使用self.

     本文转载至  http://blog.csdn.net/lvxiangan/article/details/27204265   何时使用self.在网上搜索或者论坛里的回复大多都是简简单单的说这与 ...

  6. python 基础1.1--windows/linux 下安装python

    一.windows下安装python 1>windows上python后缀是.msi的,下载下来后,直接双击运行.会在c盘生成python.exe的文件,把python.exe的文件加入到win ...

  7. php部分:网页中报表的打印,并用CSS样式控制打印的部分;

    网页中报表的打印,是通过调用window对象中的print()方法实现打印功能的: 调用浏览器本身的打印功能实现打印 <a href="#" onclick="wi ...

  8. 【百度之星复赛】T5 Valley Numer

    Valley Numer Problem Description 众所周知,度度熊非常喜欢数字. 它最近发明了一种新的数字:Valley Number,像山谷一样的数字. 当一个数字,从左到右依次看过 ...

  9. EasyNVR H5无插件摄像机直播解决方案前端解析之:videojs的使用

    video.js的基本使用方法 一.videojs的初始化加载 videojs初始化加载分为两中 1.标签式加载 在引入videojs加载文件的前提下,可以在video标签中添加属性值"da ...

  10. Git you are not allowed to push code to protected branches on this project?

    error: You are not allowed to push code to protected branches on this project....error: failed to pu ...