P3313 旅行-树剖,动态开点线段树
题目描述
题解
对每一种教派开一颗线段树,但是很明显空间会爆,所以动态开点线段树,所以空间复杂度降低到 n l o g n nlogn nlogn,然后树剖套线段树,码量稍大
代码实现
#include<bits/stdc++.h>
#define M 200009
using namespace std;
int read(){
int f=1,re=0;
char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
if(ch=='-'){f=-1,ch=getchar();}
for(;isdigit(ch);ch=getchar()) re=(re<<3)+(re<<1)+ch-'0';
return re*f;
}
char s[10];
int a[M],b[M],first[M],to[M],nxt[M],tot,cnt,tnt,rt[M],num[M],dep[M],size[M],f[M],son[M],idx[M],top[M],n,q;
struct tree{
int l,r,maxn,sum;
}tr[M*100];
void add(int x,int y){
nxt[++tot]=first[x];
first[x]=tot;
to[tot]=y;
}
void upd(int &x,int val,int l,int r,int pos){
if(!x) x=++tnt;//动态开点线段树的关键
tr[x].maxn=max(tr[x].maxn,val),tr[x].sum+=val;
if(l==r) return;
int mid=(l+r)>>1;
if(pos<=mid) upd(tr[x].l,val,l,mid,pos);
else upd(tr[x].r,val,mid+1,r,pos);
}
void del(int &k,int l,int r,int pos){
if(l==r){
tr[k].maxn=0,tr[k].sum=0;
return;
}int mid=(l+r)>>1;
if(pos<=mid) del(tr[k].l,l,mid,pos);
else del(tr[k].r,mid+1,r,pos);
tr[k].maxn=max(tr[tr[k].l].maxn,tr[tr[k].r].maxn);
tr[k].sum=tr[tr[k].l].sum+tr[tr[k].r].sum;
}
int solve1(int k,int l,int r,int ql,int qr){
if(ql<=l&&qr>=r) return tr[k].sum;
int mid=(l+r)>>1,ret=0;
if(ql<=mid) ret+=solve1(tr[k].l,l,mid,ql,qr);
if(qr>mid) ret+=solve1(tr[k].r,mid+1,r,ql,qr);
return ret;
}
int solve2(int k,int l,int r,int ql,int qr){
if(ql<=l&&qr>=r) return tr[k].maxn;
int mid=(l+r)>>1,ret=0;
if(ql<=mid) ret=max(ret,solve2(tr[k].l,l,mid,ql,qr));
if(qr>mid) ret=max(ret,solve2(tr[k].r,mid+1,r,ql,qr));
return ret;
}
void dfs1(int r,int fa){
dep[r]=dep[fa]+1;
size[r]=1;
f[r]=fa;
for(int i=first[r];i;i=nxt[i]){
int u=to[i];
if(u==fa) continue;
dfs1(u,r);
size[r]+=size[u];
if(size[u]>size[son[r]]) son[r]=u;
}
}
void dfs2(int r,int tp){
top[r]=tp;
num[r]=++cnt;
idx[num[r]]=r;
if(son[r]) dfs2(son[r],tp);
for(int i=first[r];i;i=nxt[i]){
int u=to[i];
if(!num[u]) dfs2(u,u);
}
}
int query1(int x,int y,int z){
int ans=0;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
ans+=solve1(rt[z],1,n,num[top[x]],num[x]);
x=f[top[x]];
}if(dep[x]<dep[y]) swap(x,y);
ans+=solve1(rt[z],1,n,num[y],num[x]);
return ans;
}
int query2(int x,int y,int z){
int ans=0;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
ans=max(ans,solve2(rt[z],1,n,num[top[x]],num[x]));
x=f[top[x]];
}if(dep[x]<dep[y]) swap(x,y);
ans=max(ans,solve2(rt[z],1,n,num[y],num[x]));
return ans;
}
int main(){
//freopen("1.in","r",stdin);
n=read(),q=read();
for(int i=1;i<=n;i++) b[i]=read(),a[i]=read();
for(int i=1;i<n;i++){
int x=read(),y=read();
add(x,y),add(y,x);
}dfs1(1,0),dfs2(1,1);
for(int i=1;i<=n;i++) upd(rt[a[i]],b[i],1,n,num[i]);
for(int i=1;i<=q;i++){
scanf("%s",s);
int x=read(),y=read();
if(s[0]=='C'&&s[1]=='C'){
del(rt[a[x]],1,n,num[x]);
upd(rt[y],b[x],1,n,num[x]);
a[x]=y;
}
if(s[0]=='C'&&s[1]=='W'){
del(rt[a[x]],1,n,num[x]);
upd(rt[a[x]],y,1,n,num[x]);
b[x]=y;
}if(s[0]=='Q'&&s[1]=='S') printf("%d\n",query1(x,y,a[x]));
if(s[0]=='Q'&&s[1]=='M') printf("%d\n",query2(x,y,a[x]));
}return 0;
}
启发
1,线段树的多种构建方式需掌握(包括pushup的方式,pushdown的方式,build的方式等等)