zoukankan      html  css  js  c++  java
  • 动态开点线段树

    用途

    • 需要建立多棵独立的线段树(对于每一个点建一颗权值线段树)
    • 线段树维护的值域较大(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-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
    ",f[1]);
    }
    
  • 相关阅读:
    unit 21
    unit 20
    unit 19
    第十八单元
    17 unit
    ES 中文分词
    ES 的CRUD 简单操作(小试牛刀)
    ES 必备插件的安装
    ES的安装运行
    JAVA_HOME 的设置
  • 原文地址:https://www.cnblogs.com/VIrtu0s0/p/10357299.html
Copyright © 2011-2022 走看看