描述

小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. Android Activity间动画跳转

    本博文主要介绍activity间动画跳转的问题,在这里讲一下怎么设置全部activity的动画跳转和退出跳转.事实上有些软件已经这样做了.比方我们都比較熟悉的大众点评网. 以下我们通过一个实例来看一下 ...

  2. sprint3 【每日scrum】 TD助手站立会议第六天

    站立会议 组员 昨天 今天 困难 签到 刘铸辉 (组长) 在添加日程类型处添加了选择闹钟间隔多长时间相应,并写了闹钟运行的类 在日历各个事件上都增加闹钟显示,并将数据传递给日程和时间表 感觉跟楠哥在设 ...

  3. 26最小公倍数 lowest common multiple

    题目描述 正整数A和正整数B 的最小公倍数是指 能被A和B整除的最小的正整数值,设计一个算法,求输入A和B的最小公倍数. 输入描述:输入两个正整数A和B. 输出描述:输出A和B的最小公倍数. 输入例子 ...

  4. containsKey使用方法

    作用是判断Map中是否有所需要的键值,下面是具体的代码: public static void main(String[] args) { Map<String, String> map ...

  5. 多域名THINKPHP利用MEMCACHE方式共享SESSION数据(转)

    一.问题起源 稍大一些的网站,通常都会有好几个服务器,每个服务器运行着不同功能的模块,使用不同的二级域名,而一个整体性强的网站,用户系统是统一的,即一套用户名.密码在整个网站的各个模块中都是可以登录使 ...

  6. Java编码辅助工具:Lombok —— 避免重复臃肿的代码,提高效率

    在项目开发过程中,经常会涉及到一些调整很少但又必不可少的环节,比如实体类的Getter/Setter方法,ToString方法等.这时可以使用Lombok来避免这种重复的操作,减少非核心代码的臃肿,提 ...

  7. sphinx PDF 中文

    使用reST撰写文档时,需要分多个文档时,就必须使用sphinx了,sphinx说起来很简单的,但是默认是不是支持中文的.幸好我出生的晚,sphinx现在已经支持xelatex了^_^ 安装 除了pa ...

  8. SQL Server外连接、内连接、交叉连接

    小编在做组织部维护最后收尾工作的时候,遇到了这样一个问题,须要将定性考核得分查出来.定量考核相应的数据查出来并进行得分计算.附加分查出来,最后将这三部分信息汇总之后得到总成绩,假设当中一项成绩没有进行 ...

  9. MyBatis做动态模糊查询时,like后面要不要加单引号??

    做项目遇到了个奇怪的问题,项目里面要对商品.账户.进行分别的多条件查询,于是我就采用动态多条件分页查询,起初在做账户部分的时候Mybatis是这样写的 <!-- 动态多条件分页查询 --> ...

  10. erlang处理mongodb日期时间格式data类型(原)

    在项目中,mongo中要创建日期类型,根据这个日期类型进而对mongo设置过期时间expire,加上对应的index索引自动删除. 而mongo中的日期类型,使用ISO格式,例如:ISODate(&q ...