动态开点线段树

用途

  • 需要建立多棵独立的线段树
  • 线段树维护的值域较大(1e9),但是操作次数较少(1e5)

特征

  • 类似主席树的原理,动态分配每个树节点的位置(lson[],rson[]),每次只更新一条链,但是主席树是建立一颗新的树,动态开点线段树是在一棵树上不断添加节点(还是一棵树)
  • 类似线段树的原理,push_down区间修改,push_up区间查询

例题

1.维护值域较大,线段树区间修改

cf915e

https://codeforces.com/contest/915/problem/E

题意:

q(3e5)次区间修改,将区间修改成0或1,每次修改后输出1~n(1e9)的和

题解:

  • 动态加点线段树区间修改练习
  • 通常的空间是修改次数*40(和主席树相同),根据re(少)和mle(多)修改空间

代码

#include<bits/stdc++.h>
#define all 1000000000
#define M 400005
using namespace std;
int n,q,ly[M*40],sum[M*40],ls[M*40],rs[M*40],l,r,k,dfn=0,rt=0;
void push_down(int o,int l,int r){
    if(ly[o]>=0){
        if(!ls[o])ls[o]=++dfn;
        if(!rs[o])rs[o]=++dfn;
        int mid=(l+r)/2;
        ly[ls[o]]=ly[rs[o]]=ly[o];
        sum[ls[o]]=ly[ls[o]]*(mid-l+1);
        sum[rs[o]]=ly[rs[o]]*(r-mid);
        ly[o]=-1;
    }
}

void ud(int &o,int l,int r,int L,int R,int x){
    if(!o)o=++dfn;
    if(L<=l&&r<=R){
        ly[o]=x;
        sum[o]=ly[o]*(r-l+1);
        return;
    }
    push_down(o,l,r);
    int mid=(l+r)/2;
    if(L<=mid)ud(ls[o],l,mid,L,R,x);
    if(R>mid)ud(rs[o],mid+1,r,L,R,x);
    sum[o]=sum[ls[o]]+sum[rs[o]];
}
int main(){
    cin>>n>>q;
    memset(ly,-1,sizeof(ly));
    while(q--){
        scanf("%d%d%d",&l,&r,&k);
        if(k==1)ud(rt,1,all,l,r,1);
        else ud(rt,1,all,l,r,0);
        printf("%d\n",n-sum[1]);
    }
}

2.需要建立多棵线段树

1046a

https://codeforces.com/contest/1046/problem/A

题意:

n(1e5)个机器人,每个机器人有位置x,半径r,智商值q(都是1e9),

设两个机器人能相互识别的条件是:两个机器人在彼此半径中,并且智商值相差不超过k(20),问有多少对机器人能互相识别

题解:

  • 将半径从大到小的顺序加入机器人(单点修改),线段树维护位置上机器人的个数(1e9),在加入前查询在这个机器人范围内的机器人个数(区间查询),这样做可以保证统计出来的机器人一定在相互范围内(因为后面的能看到前面的,前面的一定也能看到后面的)
  • 因为k只有20,所以可以考虑暴力,每一个智商值建一颗线段树,然后暴力枚举[q-k,q+k],范围内的线段树
  • 注意离散化智商值

代码:

#include<bits/stdc++.h>
#define ll long long
#define M 100005
using namespace std;
struct N{
   ll x,r,q;
}p[M];
int n,k,cnt=0,dfn=0,rt[M*40],rs[M*40],ls[M*40];
ll l,r,a,b,i,j,all=1e9,sum[M*40],ans;
map<ll,int>mp;

bool cmp(N a,N b){
    return a.r>b.r;
}
void ud(int &o,int l,int r,int p,int x){
    if(!o)o=++dfn;
    sum[o]+=x;
    if(l==r)return;
    int mid=(l+r)/2;
    if(p<=mid)ud(ls[o],l,mid,p,x);
    else ud(rs[o],mid+1,r,p,x);
}
ll qy(int o,int l,int r,int L,int R){
    if(!o)return 0;
    if(L<=l&&r<=R)return sum[o];
    int mid=(l+r)/2;
    ll ans=0;
    if(L<=mid)ans+=qy(ls[o],l,mid,L,R);
    if(R>mid)ans+=qy(rs[o],mid+1,r,L,R);
    return ans;
}
int main(){
    cin>>n>>k;
    for(i=0;i<n;i++){
        cin>>p[i].x>>p[i].r>>p[i].q;
    }
    sort(p,p+n,cmp);
    for(i=0;i<n;i++){
        l=max(0ll,p[i].x-p[i].r);
        r=min(all,p[i].x+p[i].r);
        a=max(0ll,p[i].q-k);
        b=p[i].q+k;
        for(j=a;j<=b;j++){
            if(mp.find(j)==mp.end())continue;
            ans+=qy(rt[mp[j]],0,all,l,r);
        }
        if(mp[p[i].q]==0)mp[p[i].q]=++cnt;
        ud(rt[mp[p[i].q]],0,all,p[i].x,1);
    }
    cout<<ans;
}

3.在线段树上dp

1111c

https://codeforces.com/contest/1111/problem/C

题意:

合并一段2^n(30)的区间,一种方法是对半分成两个区间合并,另一种方法是直接合并,代价是Ba[l,r](r-l+1)(有人)or A(没人),一共有k(1e5)个人

题解:

  • 转移方程:

    f[o]=min(a[o](r-l+1)B,f[ls[o]]+f[rs[o]])

代码:

#include<bits/stdc++.h>
#define M 100005
#define ll long long
using namespace std;
int ls[M*40],rs[M*40],rt=0,all,n,A,B,k,x,i,dfn=0;
ll a[M*40],f[M*40];
void ud(int &o,int l,int r,int p){
    if(!o)o=++dfn;
    a[o]++;
    if(l==r){
        f[o]=B*a[o];
        return;
    }
    int mid=(l+r)/2;
    if(p<=mid)ud(ls[o],l,mid,p);
    else ud(rs[o],mid+1,r,p);
    if(a[ls[o]]==0)f[ls[o]]=A;
    if(a[rs[o]]==0)f[rs[o]]=A;
    f[o]=min((r-l+1)*a[o]*B,f[ls[o]]+f[rs[o]]);
}

int main(){
    cin>>n>>k>>A>>B;
    all=1<<n;
    for(i=0;i<k;i++){
        scanf("%d",&x);
        ud(rt,1,all,x);
    }
    printf("%lld\n",f[1]);
}

原文地址:https://www.cnblogs.com/VIrtu0s0/p/10357299.html

时间: 2024-10-16 03:03:34

动态开点线段树的相关文章

【BZOJ-4636】蒟蒻的数列 动态开点线段树 ||(离散化) + 标记永久化

4636: 蒟蒻的数列 Time Limit: 30 Sec  Memory Limit: 256 MBSubmit: 247  Solved: 113[Submit][Status][Discuss] Description 蒟蒻DCrusher不仅喜欢玩扑克,还喜欢研究数列 题目描述 DCrusher有一个数列,初始值均为0,他进行N次操作,每次将数列[a,b)这个区间中所有比k小的数改为k,他想知 道N次操作后数列中所有元素的和.他还要玩其他游戏,所以这个问题留给你解决. Input 第一

Codeforces 803G Periodic RMQ Problem ST表+动态开节点线段树

思路: (我也不知道这是不是正解) ST表预处理出来原数列的两点之间的min 再搞一个动态开节点线段树 节点记录ans 和标记 lazy=-1 当前节点的ans可用  lazy=0 没被覆盖过 else 区间覆盖 push_up的时候要注意好多细节,, 数组尽量往大开 //By SiriusRen #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const in

【bzoj3939】[Usaco2015 Feb]Cow Hopscotch 动态开点线段树优化dp

题目描述 Just like humans enjoy playing the game of Hopscotch, Farmer John's cows have invented a variant of the game for themselves to play. Being played by clumsy animals weighing nearly a ton, Cow Hopscotch almost always ends in disaster, but this has

【bzoj4999】This Problem Is Too Simple! 树链剖分+动态开点线段树

题目描述 给您一颗树,每个节点有个初始值. 现在支持以下两种操作: 1. C i x(0<=x<2^31) 表示将i节点的值改为x. 2. Q i j x(0<=x<2^31) 表示询问i节点到j节点的路径上有多少个值为x的节点. 输入 第一行有两个整数N,Q(1 ≤N≤ 100,000:1 ≤Q≤ 200,000),分别表示节点个数和操作个数. 下面一行N个整数,表示初始时每个节点的初始值. 接下来N-1行,每行两个整数x,y,表示x节点与y节点之间有边直接相连(描述一颗树).

CF1045G AI robots(动态开点线段树)

题意 火星上有$N$个机器人排成一行,第$i$个机器人的位置为$x_{i}$,视野为$r_{i}$,智商为$q_{i}$.我们认为第$i$个机器人可以看到的位置是$[x_{i}-r_{i},x_{i}+r_{i}]$.如果一对机器人相互可以看到,且它们的智商$q_{i}$的差距不大于$K$,那么它们会开始聊天. 为了防止它们吵起来,请计算有多少对机器人可能会聊天. 题解 先膜一下大佬->这里 我们先按视野降序排序,这样一个一个考虑,如果后面的能看到前面,那前面的也肯定能看到后面 这样,就是对于每

CF915E Physical Education Lessons|动态开点线段树

动态开点线段树 题目暗示了区间修改,所以我们自然想到了用线段树来维护非工作日的天数. 然而我们再看一下数据范围,天数n的范围是\(1 \le n \le 10^9\),像普通线段树一样预处理显然会爆空间. 天无绝人之路,我们看一下修改个数,$1\le q \le 3 \cdot 10^5 $, 比天数少很多,这也意味着,我们预先处理好的线段树有些节点并没有用 能否优化呢?答案是肯定的,这就是动态开点线段树,顾名思义,我们只要到用某个节点的时候,才分配一个点给它,这样使得我们使用的空间大大减少.其

HDU 6183 Color it(动态开点线段树)

题目原网址:http://acm.hdu.edu.cn/showproblem.php?pid=6183 题目中文翻译: Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 132768/132768 K (Java/Others) Total Submission(s): 1677    Accepted Submission(s): 500 Problem Description 你喜欢画画吗? Little D不喜欢画画,特别

HDU 6681 Rikka with Cake(扫描线、动态开点线段树)

http://acm.hdu.edu.cn/showproblem.php?pid=6681 题意 在矩形区域内有k条射线,问这些射线将矩形分成了多少区域 题解 容易发现答案为所有射线交点个数. 按y从排序扫描矩形区域,动态开点线段树维护区间内竖线的个数,由于n,m范围较大,需要离散化处理,但这样比较麻烦且此题空间足够所以建议用动态开点. 1 #define bug(x) cout<<#x<<" is "<<x<<endl 2 #defi

Physical Education Lessons CodeForces - 915E (动态开点线段树)

Physical Education Lessons CodeForces - 915E This year Alex has finished school, and now he is a first-year student of Berland State University. For him it was a total surprise that even though he studies programming, he still has to attend physical