动态开点权值线段树

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

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 @   半步蒟蒻  阅读(156)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示