Live2d Test Env

HihoCoder1333 :平衡树(splay+lazy)(区间加值,区间删除)

 

描述

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

----------------------------------------------------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][2],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][0],rs=son[rt][1];
       siz[rt]=1,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][0],rs=son[rt][1];
          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]=0;
       }
    }
    int New_nod(int f,int k,int v)
    {
       son[++cnt][0]=son[cnt][1]=0;
       fa[cnt]=f; siz[cnt]=1;
       key[cnt]=k; val[cnt]=v;
       sum[cnt]=v; return cnt;
    }
    void init()
    {
       cnt=0;
       root=New_nod(0,-INF,0);//先加了2个边界点
       son[root][1]=New_nod(root,INF,0);
       Pushup(root);
    }
    void Rotate(int x,int c)//c==0代表右旋
    {
       int f=fa[x],nxtf=fa[f];
       Pushdown(f); Pushdown(x);
       if(nxtf!=0) son[nxtf][son[nxtf][0]!=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]][0]==f),c2=(son[f][0]==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!=0){
          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,0); 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][1];
          else now=son[now][0];
       }
       return ans;
    }
    int Findnxt(int x)//查询严格大于x的节点,可以和上面的函数合并。 
    {
       int now=root,ans;
       while(now){
          Pushdown(now);
          if(key[now]>x)ans=now,now=son[now][0];
          else now=son[now][1];
       }
       return ans;
    }
    void Delete(int l,int r)
    {
       int x=Findpre(l),y=Findnxt(r);
       Splay(x,0); Splay(y,x);
       son[y][0]=0;//内存不够时写内存池
       Pushup(y);  Pushup(x);
    }
    ll Query(int l,int r)
    {
       int x=Findpre(l),y=Findnxt(r);
       Splay(x,0); Splay(y,x);
       return sum[son[y][0]];
    }
    void Update(int l,int r,int v)
    {
       int x=Findpre(l),y=Findnxt(r);
       Splay(x,0); Splay(y,x);
       int id=son[y][0];
       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=1;i<=Tree.n;i++){
        char s[10]; int x,y,d;
        scanf("%s",s); scanf("%d%d",&x,&y);
        if(s[0]=='I')  Tree.Insert(x,y);
        else if(s[0]=='Q') printf("%lld\n",Tree.Query(x,y));
        else if(s[0]=='D')  Tree.Delete(x,y);
        else  scanf("%d",&d),Tree.Update(x,y,d);
    }
    return 0;
}

 

posted @ 2018-01-02 20:47  nimphy  阅读(743)  评论(0编辑  收藏  举报