[SDOI2014]旅行(D12 树链剖分+动态开点)
题目描述
给一棵树,每个节点有一种颜色和权值,有四种操作:将一个节点颜色改变,讲一个节点权值改变,求一上路径上某种颜色的的点的权值和,求一条路径上某种颜色的点的最大权值
N,Q < =105 , C < =105
数据保证对所有QS和QM事件,起点和终点城市的信仰相同;在任意时刻,城市的评级总是不大于104的正整数,且宗教值不大于C。
题解
一条路径上有多种颜色,不好同时维护,所以考虑一种暴力做法:树链剖分,对于每种颜色建一棵线段树,不是这个颜色在这棵线段树权值为0,因为查询是一种颜色就可以在一棵线段树上查,是可行的。
但如果将线段树开满的话是肯定会MLE的,考虑动态开点,初始会有nlgn个点,每次操作最多增加lgn个点,所以就可以保证空间。
要注意的是改变点权时,数组那里也要改,因为改颜色时要用到,不过也可以在改颜色的时候记录权值。
#include<bits/stdc++.h> using namespace std; const int maxn=100005; int n,m; int a[maxn],co[maxn]; int size[maxn],dep[maxn],fa[maxn],son[maxn]; int top[maxn],id[maxn],cnt; int num,root[maxn],ls[maxn<<5],rs[maxn<<5],sum[maxn<<5],mx[maxn<<5]; vector<int>e[maxn]; template<class T>void read(T &x){ x=0;char ch=getchar(); while(!isdigit(ch)) ch=getchar(); while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} } int max(int x,int y){return x>y ? x : y ;} void dfs(int u){ size[u]=1; for(unsigned int i=0;i<e[u].size();i++){ int v=e[u][i]; if(v==fa[u]) continue; fa[v]=u; dep[v]=dep[u]+1; dfs(v); size[u]+=size[v]; if(size[son[u]]<size[v]) son[u]=v; } } void update(int rt){ sum[rt]=sum[ls[rt]]+sum[rs[rt]]; mx[rt]=max(mx[ls[rt]],mx[rs[rt]]); } void modify(int &rt,int l,int r,int pos,int val){ if(!rt) rt=++num; if(l==r){ mx[rt]=sum[rt]=val; return ; } int mid=(l+r)>>1; if(pos<=mid) modify(ls[rt],l,mid,pos,val); else modify(rs[rt],mid+1,r,pos,val); update(rt); } void dfs(int u,int tp){ id[u]=++cnt; top[u]=tp; modify(root[co[u]],1,n,cnt,a[u]); if(!son[u]) return ; dfs(son[u],tp); for(unsigned int i=0;i<e[u].size();i++){ int v=e[u][i]; if(v==fa[u]||v==son[u]) continue; dfs(v,v); } } int querysum(int rt,int l,int r,int a_l,int a_r){ if(a_l<=l&&r<=a_r) return sum[rt]; int ret=0,mid=(l+r)>>1; if(a_l<=mid) ret+=querysum(ls[rt],l,mid,a_l,a_r); if(mid<a_r) ret+=querysum(rs[rt],mid+1,r,a_l,a_r); return ret; } int querysum(int rt,int x,int y){ int ret=0; while(top[x]!=top[y]){ if(dep[top[x]]>dep[top[y]]) swap(x,y); ret+=querysum(rt,1,n,id[top[y]],id[y]); y=fa[top[y]]; } if(dep[x]>dep[y]) swap(x,y); ret+=querysum(rt,1,n,id[x],id[y]); return ret; } int querymax(int rt,int l,int r,int a_l,int a_r){ if(a_l<=l&&r<=a_r) return mx[rt]; int ans=0,mid=(l+r)>>1; if(a_l<=mid) ans=max(ans,querymax(ls[rt],l,mid,a_l,a_r)); if(mid<a_r) ans=max(ans,querymax(rs[rt],mid+1,r,a_l,a_r)); return ans; } int querymax(int rt,int x,int y){ int ans=0; while(top[x]!=top[y]){ if(dep[top[x]]>dep[top[y]]) swap(x,y); ans=max(ans,querymax(rt,1,n,id[top[y]],id[y])); y=fa[top[y]]; } if(dep[x]>dep[y]) swap(x,y); ans=max(ans,querymax(rt,1,n,id[x],id[y])); return ans; } int main(){ read(n);read(m); for(int i=1;i<=n;i++) read(a[i]),read(co[i]); for(int i=1;i<n;i++){ int x,y; read(x);read(y); e[x].push_back(y); e[y].push_back(x); } dep[1]=1; dfs(1); dfs(1,1); for(int i=1;i<=m;i++){ char s[2];int x,y; scanf("%s",s); read(x);read(y); if(s[1]=='C'){//城市x改信y教 modify(root[co[x]],1,n,id[x],0); modify(root[y],1,n,id[x],a[x]); co[x]=y; } else if(s[1]=='W'){//城市x评级调为y modify(root[co[x]],1,n,id[x],y); a[x]=y;///注意 } else if(s[1]=='S'){//x到y的评价总和 printf("%d\n",querysum(root[co[x]],x,y)); } else {//x到y的评价最大值 printf("%d\n",querymax(root[co[x]],x,y)); } } }