动态开点权值线段树

动态开点线段树,只有需要用到一个点才新建该点,否则不进行构建,可以节省空间复杂度。

class SegmentTree{
    struct tree{
        int l,r,sz;
    }t[N<<2];
    int tot,s[N<<2],root[N],rtc=1,L=-1e7,R=1e7;
    #define l(p) (t[p].l)
    #define r(p) (t[p].r)
    #define s(p) (t[p].sz)
    inline int create(){/*新建节点*/
        return s[0]?s[s[0]--]:++tot;
    }
    inline void recycle(int p){/*内存回收*/
        t[s[++s[0]]=p]={0,0,0};
    }
    inline void pushup(int p){
        s(p)=s(l(p))+s(r(p));
    }
    void update(int&p,int l,int r,int x,int v){/*给权值x的数量加上v*/
        if(!p)p=create();
        if(l==r)return s(p)+=v,void();
        int mid=l+r>>1;
        if(x<=mid)update(l(p),l,mid,x,v);
        else update(r(p),mid+1,r,x,v);
        pushup(p);
    }
    int query(int p,int l,int r,int x,int y){/*查询权值在[x,y]之间的数的个数*/
        if(!p)return 0;
        if(x<=l&&r<=y)return s(p);
        int mid=l+r>>1,re=0;
        if(x<=mid)re+=query(l(p),l,mid,x,y);
        if(mid<y)re+=query(r(p),mid+1,r,x,y);
        return re;
    }
    void merge(int&x,int y){/*将x和y两颗线段树合并为x*/
        if(!x||!y)return x|=y,void();
        s(x)+=s(y);
        merge(l(x),l(y));
        merge(r(x),r(y));
        recycle(y);
    }
    void merge(int&p,int x,int y){/*将x和y两颗线段树合并为p*/
        if(!x||!y)return p=x|y,void();/*若其中一个为0,即某个权值线段树上没有这个点,那么无需合并,直接返回*/
        s(p=x)+=s(y);/*信息整合*/
        merge(l(p),l(p),l(y));
        merge(r(p),r(p),r(y));
        recycle(y);/*回收*/
    }
    void split(int p,int&x,int k){/*将可重集p的前k小元素分裂成p和x*/
        if(!p)return;
        x=create();
        int re=s(l(p));
        if(k>re)split(r(p),r(x),k-re);/*若p的左子树大小<k则递归右侧,要控制k!=0,即k!=re*/
        else swap(r(p),r(x));/*k>=左子树大小时,右子树均归为x*/
        if(k<re)split(l(p),l(x),k);/*递归左侧*/
        s(x)=s(p)-k;/*分裂出去的可重集x的权值就是本来全部的p的权值-k*/
        s(p)=k;/*将p的权值更新为k*/
    }
    int kth(int p,int l,int r,int k){
        if(!p)return -1;
        if(l==r)return l;/*只剩一个节点即为答案*/
        int mid=l+r>>1;
        if(s(l(p))>=k)return kth(l(p),l,mid,k);/*左子树>=k,递归左子树*/
        else return kth(r(p),mid+1,r,k-s(l(p)));/*递归右子树,k只减去左子树的大小即可*/
    }
    public:
    inline void reset(int l,int r){
        L=l,R=r;
    }
    inline void cut(int p,int l,int r){/*将可重集p中值域[l,r]之间的数分离成一个新的可重集*/
        int a=query(root[p],L,R,1,r),b=query(root[p],L,R,l,r),x;
        split(root[p],root[++rtc],a-b);/*先从p中分离出[1,r-l]*/
        split(root[rtc],x,b);/*再分离出[r-l+1,r]*/
        merge(root[p],x);/*将[1,r-l]和[r,n]进行合并*/
    }
    inline void copy(int p,int x){/*将可重集x合并入可重集p中*/
        merge(root[p],root[x]);
    }
    inline int count(int p,int l,int r){/*查询可重集p中值域[l,r]之间的数的个数*/
        return query(root[p],L,R,l,r);
    }
    inline void insert(int p,int x,int v){/*在可重集p中插入v个x*/
        update(root[p],L,R,x,v);
    }
    inline int kth(int p,int k){/*查询可重集p中的第k小元素*/
        return s(root[p])<k?-1:kth(root[p],L,R,k);
    }
    inline int rank(int p,int x){
        return query(root[p],L,R,L,x-1)+1;
    }
    inline int pre(int p,int x){
        return kth(root[p],L,R,query(root[p],L,R,L,x-1));
    }
    inline int suc(int p,int x){
        return kth(root[p],L,R,query(root[p],L,R,L,x)+1);
    }
}seg;

询问每个点子树内大于根节点权值的数的数量。普通区间和线段树,dfs时线段树合并统计答案即可。

void dfs(int x){
    for(auto y:v[x]){
        dfs(y);
        s.merge(root[x],root[y]);
    }
    ans[x]=s.query(root[x],1,1e9,a[x]+1,1e9);
}

一棵树上,每次在(x,y)的路径上每个点放上一个z类型的救济粮,问最后每个点最多的救济粮种类。每个点维护一棵权值线段树,下标为救济粮种类,区间和维护最多的救济粮的数量,查询时若数量为0,则ans标记为0,自底向上做树上前缀和与线段树合并,同时要树上差分。

#include<bits/stdc++.h>

using namespace std;

const int N=1e5+5;
int f[N][25],dep[N],root[N],ans[N];
vector<int>v[N];
struct SegmentTree{
    struct tree{
        int l,r,sum,id;
    }t[N*80];
    int tot,s[N*80];
    #define l(p) (t[p].l)
    #define r(p) (t[p].r)
    #define s(p) (t[p].sum)
    #define i(p) (t[p].id)
    inline int create(){
        return s[0]?s[s[0]--]:++tot;
    }
    inline void recycle(int p){
        t[s[++s[0]]=p]={0,0,0,0};
    }
    inline void pushup(int p){
        if(s(l(p))>=s(r(p))){
            s(p)=s(l(p));
            i(p)=i(l(p));
        }
        else{
            s(p)=s(r(p));
            i(p)=i(r(p));
        }
    }
    void update(int&p,int l,int r,int x,int v){
        if(!p)p=create();
        if(l==r)return s(p)+=v,i(p)=x,void();
        int mid=l+r>>1;
        if(x<=mid)update(l(p),l,mid,x,v);
        else update(r(p),mid+1,r,x,v);
        pushup(p);
    }
    void merge(int&x,int y,int l,int r){
        if(!x||!y)return x|=y,void();
        if(l==r)return s(x)+=s(y),void();
        int mid=l+r>>1;
        merge(l(x),l(y),l,mid);
        merge(r(x),r(y),mid+1,r);
        pushup(x);
        recycle(y);
    }
}s;
void dfs(int x,int fa){
    dep[x]=dep[fa]+1;
    f[x][0]=fa;
    for(int i=1;i<=20;i++)f[x][i]=f[f[x][i-1]][i-1];
    for(auto y:v[x])if(y^fa)dfs(y,x);
}
inline int LCA(int x,int y){
    if(dep[x]<dep[y])swap(x,y);
    for(int i=20;~i;i--)if(dep[f[x][i]]>=dep[y])x=f[x][i];
    if(x==y)return x;
    for(int i=20;~i;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    return f[x][0];
}
void calc(int x,int fa){
    for(auto y:v[x]){
        if(y==fa)continue;
        calc(y,x);
        s.merge(root[x],root[y],1,N-1);
    }
    ans[x]=s.t[root[x]].id;
    if(!s.t[root[x]].sum)ans[x]=0;
}
int main(){
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int n,m;
    cin>>n>>m;
    for(int i=1;i<n;i++){
        int a,b;
        cin>>a>>b;
        v[a].push_back(b);
        v[b].push_back(a);
    }
    dfs(1,0);
    while(m--){
        int a,b,c;
        cin>>a>>b>>c;
        int t=LCA(a,b);
        s.update(root[a],1,N-1,c,1);
        s.update(root[b],1,N-1,c,1);
        s.update(root[t],1,N-1,c,-1);
        s.update(root[f[t][0]],1,N-1,c,-1);
    }
    calc(1,0);
    for(int i=1;i<=n;i++)cout<<ans[i]<<'\n';
    return 0;
}

在两点间连边和询问一条边上的负载,一条边的负载就是所在能够联通的树上通过它的简单路径的数量。负载也就是一条边两边联通快大小的乘积,离线建立森林,每个点维护线段树的dfs序编号,加边则线段树合并,这样x或y的子树编号连续。

#include<bits/stdc++.h>

using namespace std;

const int N=1e6+6;
struct SegmentTree{
    struct tree{
        int l,r,sz;
    }t[N<<2];
    int tot,s[N<<2];
    #define l(p) (t[p].l)
    #define r(p) (t[p].r)
    #define s(p) (t[p].sz)
    inline int create(){
        return s[0]?s[s[0]--]:++tot;
    }
    inline void recycle(int p){
        t[s[++s[0]]=p]={0,0,0};
    }
    inline void pushup(int p){
        s(p)=s(l(p))+s(r(p));
    }
    void update(int&p,int l,int r,int x,int v){
        if(!p)p=create();
        if(l==r)return s(p)+=v,void();
        int mid=l+r>>1;
        if(x<=mid)update(l(p),l,mid,x,v);
        else update(r(p),mid+1,r,x,v);
        pushup(p);
    }
    int query(int p,int l,int r,int x,int y){
        if(x<=l&&r<=y)return s(p);
        int mid=l+r>>1,re=0;
        if(x<=mid)re+=query(l(p),l,mid,x,y);
        if(mid<y)re+=query(r(p),mid+1,r,x,y);
        return re;
    }
    void merge(int&x,int y,int l,int r){
        if(!x||!y)return x|=y,void();
        if(l==r)return s(x)+=s(y),void();
        int mid=l+r>>1;
        merge(l(x),l(y),l,mid);
        merge(r(x),r(y),mid+1,r);
        pushup(x);
        recycle(y);
    }
}s;
struct DSU{
    int f[N],sz[N];
    inline void init(int n){
        for(int i=1;i<=n;i++)f[i]=i;
    }
    int find(int x){
        return x==f[x]?x:f[x]=find(f[x]);
    }
    inline void merge(int x,int y){
        x=find(x),y=find(y);
        if(x==y)return;
        if(sz[x]<sz[y])x^=y^=x^=y;
        f[y]=x,sz[x]+=sz[y];
    }
}d;
int ipt[N],opt[N],dfn,dep[N],root[N],n,q;
char ch[3];
vector<int>v[N];
void dfs(int x,int fa){
    dep[x]=dep[fa]+1;
    s.update(root[x],1,n,ipt[x]=++dfn,1);
    for(auto y:v[x])if(y^fa)dfs(y,x);
    opt[x]=dfn;
}
struct node{
    int x,y,f;
}ask[N];
int main(){
    cin>>n>>q;
    for(int i=1;i<=q;i++){
        int x,y;
        cin>>ch>>x>>y;
        ask[i]={x,y,0};
        if(ch[0]=='A')ask[i].f=1,v[x].push_back(y),v[y].push_back(x);
    }
    d.init(n);
    dfs(1,0);
    for(int i=1;i<=q;i++){
        if(ask[i].f){
            int x=d.find(ask[i].x),y=d.find(ask[i].y);
            s.merge(root[x],root[y],1,n);
            d.merge(x,y);
        }
        else{
            int x=ask[i].x,y=ask[i].y;
            if(dep[x]<dep[y])x^=y^=x^=y;
            int a=d.find(x),b=s.query(root[a],1,n,ipt[x],opt[x]);
            cout<<b*(s.t[root[a]].sz-b)<<'\n';
        }
    }
    return 0;
}

维护所谓的动态图,操作1新建一个权值为x的节点,操作2连接两个点,操作3将点x所属联通块内所有点的权值对y取max,操作4将点x所属联通块内所有点的权值对y取min,操作5询问点x所属联通块内的第k小,操作6询问点x和点y所属两个联通块内所有点权值之积的大小,操作7询问点x所在联通块内点的数量。对于乘法操作,取log来转变成加法操作,并查集维护联通块。

#include<bits/stdc++.h>

using namespace std;

const int N=1e7+7;
struct SegmentTree{
    struct tree{
        int l,r,sz;
        bool del;
        double v;
    }t[N];
    int tot;
    #define l(p) (t[p].l)
    #define r(p) (t[p].r)
    #define s(p) (t[p].sz)
    #define d(p) (t[p].del)
    #define v(p) (t[p].v)
    inline void pushup(int p){
        s(p)=s(l(p))+s(r(p));
        v(p)=v(l(p))+v(r(p));
    }
    inline void pushdown(int p){
        if(!d(p))return;
        d(p)=s(l(p))=s(r(p))=v(l(p))=v(r(p))=0;
        d(l(p))=d(r(p))=1;
    }
    void update(int&p,int l,int r,int x,int v,double w){
        if(!p)p=++tot;
        if(l==r)return s(p)+=v,v(p)+=v*w,void();
        pushdown(p);
        int mid=l+r>>1;
        if(x<=mid)update(l(p),l,mid,x,v,w);
        else update(r(p),mid+1,r,x,v,w);
        pushup(p);
    }
    void del(int p,int l,int r,int x,int y){
        if(!p)return;
        if(x<=l&&r<=y)return s(p)=v(p)=0,d(p)=1,void();
        pushdown(p);
        int mid=l+r>>1;
        if(x<=mid)del(l(p),l,mid,x,y);
        if(mid<y)del(r(p),mid+1,r,x,y);
        pushup(p);
    }
    int query(int p,int l,int r,int x,int y){
        if(!p)return 0;
        if(x<=l&&r<=y)return s(p);
        pushdown(p);
        int mid=l+r>>1,re=0;
        if(x<=mid)re+=query(l(p),l,mid,x,y);
        if(mid<y)re+=query(r(p),mid+1,r,x,y);
        return re;
    }
    void merge(int&x,int y){
        if(!x||!y)return x|=y,void();
        s(x)+=s(y);
        v(x)+=v(y);
        pushdown(x);
        pushdown(y);
        merge(l(x),l(y));
        merge(r(x),r(y));
    }
    int kth(int p,int l,int r,int k){
        if(l==r)return l;
        pushdown(p);
        int mid=l+r>>1;
        if(k<=s(l(p)))return kth(l(p),l,mid,k);
        else return kth(r(p),mid+1,r,k-s(l(p)));
    }
}s;
int f[N],n,root[N],m=2147483646;
int find(int x){
    return x==f[x]?x:f[x]=find(f[x]);
}
int main(){
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int q;
    cin>>q;
	for(int i=1;i<=q;i++){
        int op,x,y,re=0;
        cin>>op>>x;
        if(op!=1&&op!=7)cin>>y;
        switch(op){
        case 1:s.update(root[++n],1,m,x,1/*数量*/,log(x)/*自己的对数,得到乘积*/),f[n]=n;break;
        case 2:x=find(x),y=find(y);if(x!=y)s.merge(root[f[y]=x],root[y]);/*线段树合并进行连边*/break;
        case 3:re=s.query(root[x=find(x)],1,m,1,y);/*查询联通块内<=y的数的数量*/s.del(root[x],1,m,1,y)/*将这些数删掉*/,s.update(root[x],1,m,y,re,log(y))/*重新加入相同数量个y*/;break;
        case 4:re=s.query(root[x=find(x)],1,m,y,m);/*查询联通块内权值在[y,m]内的数的数量*/s.del(root[x],1,m,y,m)/*将这些数删掉*/,s.update(root[x],1,m,y,re,log(y))/*重新加入相同数量个y*/;break;
		case 5:cout<<s.kth(root[find(x)],1,m,y)<<'\n';break;
        case 6:cout<<(s.t[root[find(x)]].v>s.t[root[find(y)]].v)<<'\n';break;
        case 7:cout<<(s.t[root[find(x)]].sz)<<'\n';break;
        }
	}
	return 0;	
}
posted @ 2022-11-14 17:40  半步蒟蒻  阅读(177)  评论(0)    收藏  举报