首页 > 其他分享 >(可持久化)权值线段树

(可持久化)权值线段树

时间:2024-09-29 10:24:59浏览次数:7  
标签:持久 int 线段 tr mid 权值 return now now1

权值线段树

就是把类型存在线段树上,每个下标存的是类型的数量

可以用来做离线的平衡树,如果值域范围小的话可以在线,只有一只 \(\log\)。

平衡树六种操作:

  1. 插入 \(x\)
    就是把 \(x\) 上的值加 \(1\)。
    modify(1,1,n,x,1);
    
  2. 删除一个 \(x\)
    就是把 \(x\) 上的值加 \(-1\)。
    modify(1,1,n,x,-1);
    
  3. 查询 \(x\) 的排名
    就是看 \([-\infty,x-1]\) 位置上的值的和,在树上递归。
    若 \(x>mid\) 则将左儿子的答案加上并递归右儿子;反之递归左儿子。
    int query_rk(int now,int l,int r,int x){
        if(l==r)
            return 1;
        int mid=(l+r)>>1;
        if(x<=mid)
            return query_rk(lc,l,mid,x);
        else
            return tr[lc].val+query_rk(rc,mid+1,r,x);
    }
    
  4. 查询排名为 \(x\) 的数
    在树上二分。
    若 \(x\) 大于左儿子 \([l,mid]\) 的权值 \(v_{lc}\),则跳转到右儿子 \([mid+1,r]\) 且找区间内排名为 \(x-v_{lc}\) 的数;反之递归左儿子。
    int query_num(int now,int l,int r,int x){
        if(l==r)
            return l;
        int mid=(l+r)>>1;
        if(x<=tr[lc].val)
            return query_num(lc,l,mid,x);
        else
            return query_num(rc,mid+1,r,x-tr[lc].val);
    }
    
  5. 查询 \(x\) 的前驱
    即查询排名为 \(x\) 的排名 \(-1\) 的数
    query_sum(1,1,n,query_rk(1,1,n,x)-1);
    
  6. 查询 \(x\) 的后继
    即查询排名为 \(x+1\) 的排名的数(其实查询一个不存在的数的排名会返回小于它的第一个数的最大排名 \(+1\),其实就等价于大于它的第一个数的排名),就会返回 \(x\) 的后继。
    query_sum(1,1,n,query_rk(1,1,n,x+1));
    

动态开点

为后面的线段树合并、可持久化做铺垫。

一开始不建节点,仅一个根节点代表 \([1,n]\),当出现操作需要访问 \(x\) 时,就建一条链,终点为 \([x,x]\),注意此时只能再开 \(ch[0/1]\) 代表左右儿子,复杂度仍为 \(O(F(n)\log n)\)。

int cnt; // 线段树上的点数

void modify(int &now,int l,int r,int x,int v){
    if(!now) now=++cnt; // 新建节点,加 & 保证不会访问到空节点并直接存在儿子中
    if(l==r){
        tr[now].val+=v;
        return;
    }
    int mid=(l+r)>>1;
    if(x<=mid)
        modify(lc,l,mid,x,v);
    else
        modify(rc,mid+1,x,v);
    pushup(now);
}

int query(int &now,int l,int r,int x){
    if(!now) now=++cnt;
    ......
}

线段树合并

很暴力地合并两棵树,暴力合并叶子结点的信息,然后上传。

对于权值线段树更简单,直接把每个位置上的值对应加起来即可,时间复杂度 \(O(n)\)。


int merge(int now1,int now2,int l,int r){
    if(!now1) return now2;
    if(!now2) return now1;
    if(l==r){
        tr[now1].val+=tr[now2].val;
        return now1;
    }
    int mid=(l+r)>>1;
    tr[now1].ls=merge(tr[now1].ls,tr[now2].ls,l,mid);
    tr[now1].rs=merge(tr[now1].rs,tr[now2].rs,mid+1,r);
    pushup(now1);
    return now1;
}

P4556 [Vani有约会] 雨天的尾巴

有一棵 \(n\) 个节点的树,有 \(m\) 个操作:

  • 将 \((u,v)\) 路径上的点的颜色 \(c\) 加 \(1\)。

求操作完后,每个节点最多的颜色是什么,若有数量相同,则取小的。

\(n,c\le 10^5\)

考虑对每个节点建一棵权值线段树,然后树剖维护修改,可以做到 \(O(n\log^2 n)\),需要动态开点。

由于是离线处理,所以可以在树上差分把区间修改变成单点修改,最后 DFS pushup 一下,每次合并权值线段树,时间复杂度 \(O(n\log n)\)。

code
#include<bits/stdc++.h>
using namespace std;
#define lc tr[now].ls
#define rc tr[now].rs
const int maxn=2e5+3;
const int N=1e5;
struct node{
    int ls,rs,val,mx,mxpos;
}tr[maxn<<5];
int cnt;
void pushup(int now){
    tr[now].val=tr[lc].val+tr[rc].val;
    if(tr[lc].mx>=tr[rc].mx){
        tr[now].mx=tr[lc].mx;
        tr[now].mxpos=tr[lc].mxpos;
    }else{
        tr[now].mx=tr[rc].mx;
        tr[now].mxpos=tr[rc].mxpos;
    }
}
void modify(int &now,int l,int r,int x,int v){
    if(!now) now=++cnt;
    if(l==r){
        tr[now].val+=v;
        tr[now].mx=tr[now].val;
        tr[now].mxpos=(tr[now].mx?l:0);
        return;
    }
    int mid=(l+r)>>1;
    if(x<=mid) modify(lc,l,mid,x,v);
    else modify(rc,mid+1,r,x,v);
    pushup(now);
}
int merge(int now1,int now2,int l,int r){
    if(!now1) return now2;
    if(!now2) return now1;
    if(l==r){
        tr[now1].val+=tr[now2].val;
        tr[now1].mx=tr[now1].val;
        tr[now1].mxpos=(tr[now1].mx?l:0);
        return now1;
    }
    int mid=(l+r)>>1;
    tr[now1].ls=merge(tr[now1].ls,tr[now2].ls,l,mid);
    tr[now1].rs=merge(tr[now1].rs,tr[now2].rs,mid+1,r);
    pushup(now1);
    return now1;
}
int n,m;
int head[maxn]; // 每棵线段树根节点
vector<int>e[maxn];
int dep[maxn],fa[maxn][21];
void dfs1(int u,int Fa){
    fa[u][0]=Fa;
    dep[u]=(u==1?1:dep[Fa]+1);
    for(int v:e[u])
        if(v!=Fa)
            dfs1(v,u);
}
void init(){
    dfs1(1,0);
    for(int i=1;i<19;i++){
        for(int j=1;j<=n;j++){
            int Fa=fa[j][i-1];
            if(~Fa) fa[j][i]=fa[Fa][i-1];
            else fa[j][i]=Fa;
        }
    }
}
int lca(int u,int v){
    if(dep[u]>dep[v]) swap(u,v);
    for(int i=0;i<19;i++)
        if((dep[v]-dep[u])>>i&1) v=fa[v][i];
    if(u==v) return u;
    for(int i=18;~i;i--)
        if(fa[u][i]!=fa[v][i])
            u=fa[u][i], v=fa[v][i];
    return fa[u][0];
}
void dfs(int u,int fa){
    for(int v:e[u])
        if(v!=fa){
            dfs(v,u);
            head[u]=merge(head[u],head[v],1,N);
        }
}
signed main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        head[i]=++cnt;
    for(int i=1,u,v;i<n;i++){
        cin>>u>>v;
        e[u].emplace_back(v);
        e[v].emplace_back(u);
    }
    init();
    for(int i=1,u,v,c;i<=m;i++){
        cin>>u>>v>>c;
        int w=lca(u,v);
        modify(head[fa[w][0]],1,N,c,-1);
        modify(head[w],1,N,c,-1);
        modify(head[u],1,N,c,1);
        modify(head[v],1,N,c,1);
    }
    dfs(1,0);
    for(int i=1;i<=n;i++)
        cout<<tr[head[i]].mxpos<<'\n';
    return 0;
}

标签:持久,int,线段,tr,mid,权值,return,now,now1
From: https://www.cnblogs.com/DEV3937/p/18437895

相关文章

  • (nice!!!)LeetCode 2286. 以组为单位订音乐会的门票(线段树)
    题目:2286.以组为单位订音乐会的门票思路:线段树做法。(线段树)acwing1265.数星星classBookMyShow{public: //结构体typedefstructNode{intmn=0;//最小空位编号longlongsum=0;//非空位置之和}node; //n,mintN,M;......
  • 学习011-03-02 Base Persistent Classes(基本持久化类)
    BasePersistentClasses(基本持久化类)ThistopicdescribesthebasepersistentclassesthatcanbeusedinXAFapplicationswhencreatingadatamodelwithXPO.本主题介绍在使用XPO创建数据模型时可在XAF应用程序中使用的基本持久类。Thefollowingtablelists......
  • 学习011-03-03 Relationships Between Persistent Objects in Code and UI(代码和用户
    RelationshipsBetweenPersistentObjectsinCodeandUI(代码和用户界面中持久对象之间的关系)Whendesigningabusinessmodel,itcanbenecessarytosetspecificrelationshipsbetweenbusinessobjects.Thistopicdescribeshowtosettheserelationshipsbe......
  • nacos配置持久化到mysql数据库
    以版本2.4.1为例,要实现Nacos2.4.1的配置持久化,你需要按照以下步骤操作:准备数据库:首先,确保你已经安装并配置好了MySQL数据库,并且版本符合Nacos的要求(MySQL5.6及以上)。创建数据库:在MySQL中创建一个新的数据库,例如命名为nacos。执行SQL脚本:从Nacos的conf......
  • Docker容器启动Redis设置密码并持久化
    启动命令dockerrun--namewh-redis-p6379:6379-v/root/RedisData:/data-d--restartunless-stoppedredis--appendonlyyes--requirepass'Your-password'dockerrun:启动一个新的Docker容器。--namewh-redis:给容器指定一个名称,容器名为wh-redis。指定名......
  • Redis实战--Redis的数据持久化与搭建Redis主从复制模式和搭建Redis的哨兵模式
            Redis作为一个高性能的key-value数据库,广泛应用于缓存、消息队列、排行榜等场景。然而,Redis是基于内存的数据库,这意味着一旦服务器宕机,内存中的数据就会丢失。为了解决这个问题,Redis提供了数据持久化的机制,包括RDB和AOF两种方式。此外,为了提高数据的可用性和可......
  • noip2014联合权值
    noip2014联合权值题目描述无向连通图G有n个点,n-1条边。点从1到n依次编号,编号为i的点的权值为Wi,每条边的长度均为1。图上两点(u,v)的距离定义为u点到v点的最短距离。对于图G上的点对(u,v),若它们的距离为2,则它们之间会产生Wu×Wv的联合权值。请问图G上所有可产生......
  • 线段树进阶应用学习笔记(一)(2024.7.19)(2024.8.22)
    线段树优化建图算法流程复杂度分析例题一#include<bits/stdc++.h>usingnamespacestd;#defineintlonglongconstintN=5e5,M=5e6+9;structEdge{ intv,w,nex;}e[M];inthead[M],ecnt;voidAddEdge(intu,intv,intw){ e[++ecnt]=Edge{v,w,hea......
  • Redis数据持久化RDB和AOF
    Redis数据持久化RDB和AOFRedis支持两种持久化机制:RDB(快照)和AOF(追加文件)。它们各有优缺点,适用于不同的场景。RDB(快照)特点:快照方式:在指定的时间间隔内(例如每隔5分钟或每隔1000个写入命令),Redis会生成当前内存数据的快照,并将其保存为RDB文件。文件格式:RDB文件是......
  • java 弧度转多线段
    一:概述弧度和线段是数学和计算机图形学中的基本概念,它们在处理图形变换、动画制作以及游戏开发等领域中有着广泛的应用。在Java中,我们可以通过多种方式将弧度转换为多线段,以实现不同的图形效果。本文将介绍几种不同的方法,并提供实际的案例分析。二:具体说明<1>基本概念弧度:表示一个......