首页 > 其他分享 >BZOJ4399 魔法少女LJJ —— 线段树合并

BZOJ4399 魔法少女LJJ —— 线段树合并

时间:2025-01-09 22:11:10浏览次数:1  
标签:rt LJJ return int fin 线段 BZOJ4399 INF root

题意

提示

对100%的数据 0<=m<=400000,c<=7,所有出现的数均<=1000000000,所有出现的点保证存在

【HINT】请认真阅读题面 考语文


分析

由于只有合并,没有分裂,所以只需要考虑合并联通块中的信息即可。

具体而言,在联通块的根对应的线段树下标存储该联通块下元素对应的权值。

直接线段树合并,更新新联通块的根即可。


#include<bits/stdc++.h>
using namespace std;

const int N=4e5+100,INF=1e9;
typedef long long ll;

struct segtree
{
    struct node
    {
        int siz,tag,lc,rc;
        double val;
    }s[N<<6];
    int tot,root[N],all,f[N];

    int fin(int x){return (x==f[x])?(x):(f[x]=fin(f[x]));}

    void pushup(int i)
    {
        s[i].siz=s[ s[i].lc ].siz+s[ s[i].rc ].siz;
        s[i].val=s[ s[i].lc ].val+s[ s[i].rc ].val;
    }

    void upd(int &i,int l,int r,int x,int y)
    {
        if(!i)
            i=++tot;
        if(l==r)
        {
            s[i].siz+=y;
            s[i].val+=1.0*y*(log(x));
            return ;
        }
        pushdown(i);
        int mid=(l+r)>>1;
        if(x<=mid)
            upd(s[i].lc,l,mid,x,y);
        else
            upd(s[i].rc,mid+1,r,x,y);
        pushup(i);

    }

    void pushtag(int &i)
    {
        if(!i)
            return ;
        s[i].tag=1;
        s[i].val=s[i].siz=0;
    }

    void pushdown(int i)
    {
        if(!s[i].tag)
            return ;
        pushtag(s[i].lc);
        pushtag(s[i].rc);
        s[i].tag=0;
    }

    int quek(int i,int l,int r,int k)
    {
        if(l==r)
            return l;
        pushdown(i);
        int mid=(l+r)>>1,num=s[ s[i].lc ].siz;
        if(num>=k)
            return quek(s[i].lc,l,mid,k);
        else
            return quek(s[i].rc,mid+1,r,k-num);
    }

    int que0(int i,int l,int r,int x,int y)
    {
        if(!i || s[i].siz==0)
            return 0;
        if(l>=x && r<=y)
            return s[i].siz;
        pushdown(i);
        int mid=(l+r)>>1,sum=0;
        if(x<=mid)
            sum+=que0(s[i].lc,l,mid,x,y);
        if(y>mid)
            sum+=que0(s[i].rc,mid+1,r,x,y);
        return sum;
    }

    void cl(int i,int l,int r,int x,int y)
    {
        if(!i)
            return ;
        if(l>=x && r<=y)
        {
            pushtag(i);
            return ;
        }
        int mid=(l+r)>>1;
        pushdown(i);
        if(x<=mid)
            cl(s[i].lc,l,mid,x,y);
        if(y>mid)
            cl(s[i].rc,mid+1,r,x,y);
        pushup(i);
    }

    void create(int x)
    {
        ++all;
        f[all]=all;
        upd(root[all],1,INF,x,1);
    }

    void merg(int &i,int &j,int l,int r)
    {
        if(!i || !j)
        {
            i+=j;
            return ;
        }
        if(l==r)
        {
            s[i].siz+=s[j].siz;
            s[i].val+=s[j].val;
            return ;
        }
        pushdown(i);
        pushdown(j);
        int mid=(l+r)>>1;
        merg(s[i].lc,s[j].lc,l,mid);
        merg(s[i].rc,s[j].rc,mid+1,r);
        pushup(i);
    }

    int qsiz(int x)
    {
        x=fin(x);
        return s[ root[x] ].siz;
    }

    void grem(int x,int y)
    {

        x=fin(x);
        y=fin(y);
        if(x==y)
            return ;
        f[y]=x;
        merg(root[x],root[y],1,INF);
    }

    void mov(int rt,int x,int y,int z)
    {
        rt=fin(rt);
        if( y>INF || (x>y) )
            return ;
        int num=que0(root[rt],1,INF,x,y);
        upd(root[rt],1,INF,z,num);
        cl(root[rt],1,INF,x,y);
    }

    int gotk(int rt,int k)
    {
        rt=fin(rt);
        return quek(root[rt],1,INF,k);
    }

    int com(int x,int y)
    {
        x=fin(x);
        y=fin(y);
        return s[ root[x] ].val>s[ root[y] ].val;
    }

}T;
int n;
void work()
{
    cin>>n;
    while(n--)
    {
        int opt,x,y,ans=0;
        scanf("%d%d",&opt,&x);
        if(opt==1)
            T.create(x);
        else if(opt==7)
        {

            ans=T.qsiz(x);
            if(ans<0)printf("%d ",opt);
            printf("%d\n",ans);
        }
        else
        {
            scanf("%d",&y);
            if(opt==2)
                T.grem(x,y);
            else if(opt==3)
                T.mov(x,1,y-1,y);
            else if(opt==4)
                T.mov(x,y+1,INF,y);
            else
            {
                if(opt==5)
                    ans=T.gotk(x,y);//wrong
                else
                    ans=T.com(x,y);
                if(ans<0)printf("%d ",opt);
                printf("%d\n",ans);
            }

        }
    }
}

int main()
{
    work();
    return 0;
}

标签:rt,LJJ,return,int,fin,线段,BZOJ4399,INF,root
From: https://www.cnblogs.com/Glowingfire/p/18662985

相关文章

  • 一种调试 线段树 / Treap / Splay / 左偏树 / LCT 等树形结构的技巧
    前言如果我们需要观察程序运行过程中,某一个变量、某一个序列的变化情况,你可以在修改的地方打断点debug,或者直接在需要的地方输出就行了。但是对于一些树形结构,我们不好将其直观地呈现出来,常常只是输出每一个结点的值,但是这就丢失了结点之间的连边情况。有时候不得不手动画图。......
  • 线段树合并
    前言:模拟赛考到了此类题目,出现过很多次了,就去小小学习了一下,发现其实挺简单的。前置知识:线段树(这个知识点如果没有掌握先自行学习,学完再来看,因为本人太弱了,没有写这个的讲解)动态开点线段树(这个知识点如果没有掌握先自行学习,学完再来看)引入:在一些树形结构中子节点的贡......
  • 洛谷题单指南-线段树的进阶用法-P4093 [HEOI2016/TJOI2016] 序列
    原题链接:https://www.luogu.com.cn/problem/P4093题意解读:一个序列,m个变化,求任意一个变化后不受影响的最长上升子序列长度。解题思路:设原序列为a[N],原序列经过变化后能得到的最大值序列为maxa[N],最小值序列为mina[N]设f[i]表示以第i个数结尾的最长不降子序列长度有f[i]=max......
  • 线段树
    前言线段树用来解决区间问题。包括并不限于:\(RMQ\),整数区间求和等问题。通常的:可用来求下标连续区间二元运算后结果(比如群\((\mathbb{G},*)\))。而线段树的题一般用来选择合适的集合(比如矩阵,线性基等)。并在合适的时间复杂度内维护二元运算\(*\)。同时可以理解为分治的一种。......
  • 线段树优化 dp 学习笔记
    到底是什么算法让我觉得两道题就足以让我写一篇学习笔记呢?虽然两年半以前写过一道dp,正解的优化是单调队列,但是我拿线段树过了(卡着空间过的),所以那个dp并不能叫线段树优化dp。CF115ELinearKingdomRaces这个算是最“原汁原味”线段树优化dp。设\(dp_{i,j}\)表示第\(j\)......
  • 模仿jiangly封装的线段树单点修改模板
    https://codeforces.com/contest/2057/problem/D#include<bits/stdc++.h>usingnamespacestd;#definexfirst#defineysecond#defineintlonglong#defineendl'\n'constintN=1e6+10,mod=998244353,INF=1e16;typedefpair<int,int>PI......
  • 线段树进阶练习专题
    小白逛公园题目大意:求一段区间里最大子段和思路:有空补(code:#include<bits/stdc++.h>usingnamespacestd;constintMAXN=500100;intm,n;inta[MAXN];inlineintread(){ intx=0,f=1; charch=getchar(); while(ch>'9'||ch<'0'){ if(ch==&#......
  • P2572 [SCOI2010] 序列操作 —— 线段树
    题意原题给定一个0/1序列,初始全为零要求分别实现:-区间赋值-区间取反-询问区间1的个数-询问区间为1的最大子段和分析形式化地定义变量,我们记下区间的0/1个数,0/1最大字段和,赋值与取反标记。赋值的标记优先级大于取反标记,取反直接把区间赋值标记,区间0/1个数和最大子段和交换......
  • 线段树合并学习笔记
    前言模拟赛solution里说只需要利用线段树合并的思想……但是我不会线段树合并,就先学习了线段树合并。引入线段树合并是把每个对应节点合并。两棵线段树都有某个节点,就是把这两个点合成一个点;只有一棵线段树有某个节点,合并出来的线段树的这个节点就是这个唯一的节点。......
  • 线段树优化建图
    更新日志2025/01/05:开工。概念利用线段树优化建图。一般情况下,会出现点和区间或区间和区间连边的情况,就可以考虑线段树优化建图了。思路开两棵线段树,一棵储存入边,一棵储存出边。每个节点都代表对应的区间。入树中每个点都指向其子节点,出树中相反。区间连边时,在对应线......