【bzoj2333 & luoguP3273】棘手的操作(线段树合并)

  题目传送门:bzoj2333 luoguP3273

  这操作还真“棘手”。。听说这题是可并堆题?然而我不会可并堆。于是我就写了线段数合并,然后调了一晚上,数据结构毁一生!!!QAQ……

  其实这题也可以把合并强行看成树上的关系然后dfs序后直接线段树的,然而我菜啊。。看到连边就只能想到线段树合并。

  首先用并查集维护图的连通性,然后对于每个连通块建一棵下标为点的编号的线段树,于是:

  U=合并两棵树;

  A1:单点加;

  A2:区间加;

  A3:因为是整体加,所以我们可以维护一个delta表示当前每个数被整体加了多少,然后输出时直接加上就好了;

  F1:单点查询;

  F2:区间查询;

  然而还有一个F3整体查询最大值很难处理。于是我再开了一颗线段树,维护每个连通块内的最大值,修改时顺便在上面修改信息,F3操作可以直接在树上查询。

  时间复杂度$ O(n \log n) $,然而常数极大。

  两颗线段树+奇奇怪怪的维护方法,使写出来的代码膨胀到了3.6kB……然后,调试++,and then,(如果你写的不优美)MLE+TLE。经过了一番痛苦的调试后,终于过了。。。QAQ

  又臭又长的代码:

#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<algorithm>
#define ll long long
#define inf 0x3f3f3f3f
#define maxn 300010
inline ll read()
{
    ll x=0; char c=getchar(),f=1;
    for(;c<‘0‘||‘9‘<c;c=getchar())if(c==‘-‘)f=-1;
    for(;‘0‘<=c&&c<=‘9‘;c=getchar())x=x*10+c-‘0‘;
    return x*f;
}
struct segment_tree1{
    struct point{
        int lc,rc,mx,delta;
    }sgt[20*maxn];
    int tot;
    void add(int now,int l,int r,int x,int y,int k)
    {
        if(x<=l&&r<=y)sgt[now].delta+=k,sgt[now].mx+=k;
        else{
            int mid=(l+r)>>1;
            if(x<=mid){
                if(!sgt[now].lc)sgt[now].lc=++tot;
                add(sgt[now].lc,l,mid,x,y,k);
            }
            if(mid<y){
                if(!sgt[now].rc)sgt[now].rc=++tot;
                add(sgt[now].rc,mid+1,r,x,y,k);
            }
            sgt[now].mx=std::max(sgt[sgt[now].lc].mx,sgt[sgt[now].rc].mx);
            if(sgt[now].mx!=-inf)sgt[now].mx+=sgt[now].delta;
        }
    }
    int getmax(int now,int l,int r,int x,int y)
    {
        if(x<=l&&r<=y)return sgt[now].mx;
        else{
            int mid=(l+r)>>1,mx=-inf;
            if(x<=mid&&sgt[now].lc)mx=std::max(mx,getmax(sgt[now].lc,l,mid,x,y));
            if(mid<y&&sgt[now].rc)mx=std::max(mx,getmax(sgt[now].rc,mid+1,r,x,y));
            return mx+sgt[now].delta;
        }
    }
    void merge(int x,int y,int d)
    {
        d+=sgt[y].delta-sgt[x].delta;
        if(!sgt[x].lc)sgt[x].lc=sgt[y].lc,sgt[sgt[x].lc].delta+=d,sgt[sgt[x].lc].mx+=d;
        else if(sgt[y].lc)merge(sgt[x].lc,sgt[y].lc,d);
        if(!sgt[x].rc)sgt[x].rc=sgt[y].rc,sgt[sgt[x].rc].delta+=d,sgt[sgt[x].rc].mx+=d;
        else if(sgt[y].rc)merge(sgt[x].rc,sgt[y].rc,d);
        sgt[x].mx=std::max(sgt[sgt[x].lc].mx,sgt[sgt[x].rc].mx)+sgt[x].delta;
    }
}t1;
struct segment_tree2{
    struct point{
        int mx,delta;
    }sgt[4*maxn];
    void add(int now,int l,int r,int x,int y,int k)
    {
        if(x<=l&&r<=y)sgt[now].delta+=k,sgt[now].mx+=k;
        else{
            int mid=(l+r)>>1;
            if(x<=mid)add(now<<1,l,mid,x,y,k);
            if(mid<y)add(now<<1|1,mid+1,r,x,y,k);
            sgt[now].mx=std::max(sgt[now<<1].mx,sgt[now<<1|1].mx);
            if(sgt[now].mx!=-inf)sgt[now].mx+=sgt[now].delta;
        }
    }
    int getmax(int now,int l,int r,int x,int y)
    {
        if(x<=l&&r<=y)return sgt[now].mx;
        else{
            int mid=(l+r)>>1,mx=-inf;
            if(x<=mid)mx=std::max(mx,getmax(now<<1,l,mid,x,y));
            if(mid<y)mx=std::max(mx,getmax(now<<1|1,mid+1,r,x,y));
            return sgt[now].delta+mx;
        }
    }
}t2;
int rt[maxn],fa[maxn],a[maxn],delta;
int n,m;
char op[5];
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
int main()
{
    n=read();
    t1.tot=0; delta=0;
    t1.sgt[0].mx=t2.sgt[0].mx=-inf;
    for(int i=1;i<=n;i++){
        a[i]=read();
        rt[i]=++t1.tot; fa[i]=i;
        t1.add(rt[i],1,n,i,i,a[i]);
        t2.add(1,1,n,i,i,a[i]);
    }
    m=read();
    for(int i=1;i<=m;i++){
        scanf("%s",op);
        if(op[0]==‘U‘){
            int x=read(),y=read();
            x=find(x); y=find(y);
            if(x!=y){
                fa[y]=x; t1.merge(rt[x],rt[y],0);
                t2.add(1,1,n,x,x,std::max(a[x],a[y])-a[x]); t2.add(1,1,n,y,y,-a[y]-inf);
                a[x]=std::max(a[x],a[y]); a[y]=-inf;
            }
        }
        else if(op[0]==‘A‘){
            if(op[1]==‘1‘){
                int x=read(),v=read(),fx=find(x);
                t1.add(rt[fx],1,n,x,x,v);
                int t=t1.getmax(rt[fx],1,n,1,n);
                t2.add(1,1,n,fx,fx,t-a[fx]);
                a[fx]=t;
            }
            else if(op[1]==‘2‘){
                int x=read(),v=read(),fx=find(x);
                t1.add(rt[fx],1,n,1,n,v);
                t2.add(1,1,n,fx,fx,v);
                a[fx]+=v;
            }
            else{
                int v=read();
                delta+=v;
            }
        }
        else{
            if(op[1]==‘1‘){
                int x=read(),fx=find(x);
                printf("%d\n",t1.getmax(rt[fx],1,n,x,x)+delta);
            }
            else if(op[1]==‘2‘){
                int x=read(),fx=find(x);
                printf("%d\n",a[fx]+delta);
            }
            else printf("%d\n",t2.getmax(1,1,n,1,n)+delta);
        }
    }
}

bzoj2333 & luoguP3273

原文地址:https://www.cnblogs.com/quzhizhou/p/10099529.html

时间: 2024-08-06 19:53:04

【bzoj2333 & luoguP3273】棘手的操作(线段树合并)的相关文章

神奇的操作——线段树合并(例题: BZOJ2212)

什么是线段树合并? 首先你需要动态开点的线段树.(对每个节点维护左儿子.右儿子.存储的数据,然后要修改某儿子所在的区间中的数据的时候再创建该节点.) 考虑这样一个问题: 你现在有两棵权值线段树(大概是用来维护一个有很多数的可重集合那种线段树,若某节点对应区间是\([l, r]\),则它存储的数据是集合中\(\ge l\).\(\le r\)的数的个数),现在你想把它们俩合并,得到一棵新的线段树.你要怎么做呢? 提供这样一种算法(tree(x, y, z)表示一个左儿子是x.右儿子是y.数据是z的

bzoj 2333 棘手的操作(线段树)

题外话 昨天粗去浪了一天,打麻将输了一下午真是拙计啊 这个题号2333真是2333,有爱无比 晚上回家把这题写了,1A,还算不错 Description 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作: U x y:加一条边,连接第x个节点和第y个节点 A1 x v:将第x个节点的权值增加v A2 x v:将第x个节点所在的连通块的所有节点的权值都增加v A3 v:将所有节点的权值都增加v F1 x:输出第x个节点当前的权值 F2 x:

【BZOJ2333】棘手的操作(左偏树,STL)

[BZOJ2333]棘手的操作(左偏树,STL) 题面 BZOJ上看把... 题解 正如这题的题号 我只能\(2333\) 神TM棘手的题目... 前面的单点/联通块操作 很显然是一个左偏树+标记 (确实很显然,只是写死人...) 然后对于全局的最大值而言 搞一个\(multi\)来水 看起来真的简单.. 写起来真的想死... 记住:要特判一下已经联通的块就不要再去\(Merge\)了 #include<iostream> #include<cstdio> #include<

bzoj2333[SCOI2011]棘手的操作

bzoj2333[SCOI2011]棘手的操作 题意: 有N个节点,M个操作:连接两个节点.单个节点的权值增加v.节点所在的连通块的所有节点的权值增加v.所有节点的权值增加v.询问节点当前的权值.询问节点所在的连通块中权值最大的节点的权值.询问所有节点中权值最大的节点的权值.N,M≤300000 题解: 可并堆,虽然听说配对堆非常快,但教程太少了不会写,所以去学了斜堆,比较好写.斜堆实际上是一棵二叉树,核心是合并操作,这是一个递归过程,有点像treap的删除操作.斜堆保证复杂度的方法是每次递归合

【BZOJ4399】魔法少女LJJ 线段树合并

[BZOJ4399]魔法少女LJJ Description 在森林中见过会动的树,在沙漠中见过会动的仙人掌过后,魔法少女LJJ已经觉得自己见过世界上的所有稀奇古怪的事情了LJJ感叹道“这里真是个迷人的绿色世界,空气清新.淡雅,到处散发着醉人的奶浆味:小猴在枝头悠来荡去,好不自在:各式各样的鲜花争相开放,各种树枝的枝头挂满沉甸甸的野果:鸟儿的歌声婉转动听,小河里飘着落下的花瓣真是人间仙境”SHY觉得LJJ还是太naive,一天,SHY带着自己心爱的图找到LJJ,对LJJ说:“既然你已经见识过动态树

【BZOJ2733】永无乡[splay启发式合并or线段树合并]

题目大意:给你一些点,修改是在在两个点之间连一条无向边,查询时求某个点能走到的点中重要度第k大的点.题目中给定的是每个节点的排名,所以实际上是求第k小:题目求的是编号,不是重要度的排名.我一开始差点被这坑了. 网址:http://www.lydsy.com/JudgeOnline/problem.php?id=2733 这道题似乎挺经典的(至少我看许多神犇很早就做了这道题).这道题有两种写法:并查集+(splay启发式合并or线段树合并).我写的是线段树合并,因为--splay不会打+懒得学.

[XJOI NOI2015模拟题13] C 白黑树 【线段树合并】

题目链接:XJOI - NOI2015-13 - C 题目分析 使用神奇的线段树合并在 O(nlogn) 的时间复杂度内解决这道题目. 对树上的每个点都建立一棵线段树,key是时间(即第几次操作),动态开点. 线段树的节点维护两个值,一个是这段时间内的 1 操作个数,另一个是这段时间内变化的黑色节点权值和. 在处理所有操作的时候,每棵线段树都是仅代表树上的一个点,因此线段树的每个节点维护的就是这段时间内以这个点为 a 的 1 操作个数和这段时间内这个点的黑色节点权值和(这个点 x 由黑变白就 -

bzoj 4631: 踩气球 线段树合并

4631: 踩气球 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 265  Solved: 136[Submit][Status][Discuss] Description 六一儿童节到了, SHUXK 被迫陪着M个熊孩子玩一个无聊的游戏:有N个盒子从左到右排成一排,第i个盒子里装着Ai个气球. SHUXK 要进行Q次操作,每次从某一个盒子里拿出一个没被踩爆的气球,然后熊孩子们就会立刻把它踩爆. 这M个熊孩子每个人都指定了一个盒子区间[Li, R

bzoj2333 [SCOI2011]棘手的操作(洛谷3273)

题目描述 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作:U x y: 加一条边,连接第x个节点和第y个节点A1 x v: 将第x个节点的权值增加vA2 x v: 将第x个节点所在的连通块的所有节点的权值都增加vA3 v: 将所有节点的权值都增加vF1 x: 输出第x个节点当前的权值F2 x: 输出第x个节点所在的连通块中,权值最大的节点的权值F3: 输出所有节点中,权值最大的节点的权值 输入输出格式 输入格式: 输入的第一行是一个整数