[bzoj4372] 烁烁的游戏 (点分治+线段树)
题目
给一颗n个节点的树,边权均为1,初始点权均为0,m次操作:
Q x:询问x的点权。
M x d w:将树上与节点x距离不超过d的节点的点权均加上w。
$n,m<=10^5$
题解
树上的题,要联想到用树链剖分或是点分治处理。
这题先用点分治建点分树,
修改时不能暴力修改,考虑打标记,
但一个点可能有多重标记,处理起来比较复杂
其实每次修改(M x d w)对于这个点p而言,我们只需要知道d-dis(x,p)和w是多少即可。
考虑用线段树作为标记。
每个点塞一颗线段树,以子树内的点(新的树)到这个点的距离为下标,修改时将$[0,d-dis(x,p)]$区间增加w即可
每次修改时就一路往上更新
这样时空复杂度是$O(n*log_n^2)$的
另外,注意下图,假设现在是 (M,x,4,w),
那么在rt1处可以访问到p点和红色点,在rt2处可以访问到p点和黄色点,
这样若是查询时经过p点,那么会减两次w
所以我们要在rt1处更新时将p点排除在外
具体的话就是在原基础上将$ [0,d-dis(rt2,x)-dis(rt1,rt2)]$减去w
为了节省时间,我们可以标记永久化
查询时就从那个点开始一路向上,累加一路的标记。
代码
#include <iostream> #include <cstdio> #include <vector> #include <stack> using namespace std; #define N 110000 int dep[N],root,sz[N],val[25602560],sum,fa[N],up[N][20],rt1[N],rt2[N],cnt,lc[25602560],rc[25602560],n,mson[N]; bool isroot[N]; vector<int> vec[N]; void get_dep(int id,int from) { dep[id]=dep[from]+1; up[id][0]=from; for(int i=1;i<20;i++) up[id][i]=up[up[id][i-1]][i-1]; if(id==43244) int asfd=0; for(int i=0;i<vec[id].size();i++) { int to=vec[id][i]; if(to==from) continue; get_dep(to,id); } } int get_dis(int a,int b) { if(dep[a]<dep[b]) swap(a,b); int tot=0; for(int i=19;i>=0;i--) if(dep[up[a][i]]>=dep[b]) a=up[a][i],tot+=(1<<i); if(a==b) return tot; for(int i=19;i>=0;i--) if(up[a][i]!=up[b][i]) a=up[a][i],b=up[b][i],tot+=(1<<(i+1)); return tot+2; } void getroot(int id,int from) { sz[id]=1; mson[id]=0; for(int i=0;i<vec[id].size();i++) { int to=vec[id][i]; if(isroot[to]||to==from)continue; getroot(to,id); sz[id]+=sz[to]; mson[id]=max(mson[id],sz[to]); } mson[id]=max(mson[id],sum-sz[id]); if(mson[id]<mson[root]) { root=id; } } void build(int id) { isroot[id]=true; for(int i=0;i<vec[id].size();i++) { int to=vec[id][i]; if(isroot[to])continue; sum=sz[to]; if(id==922) { int al=1; } root=0; getroot(to,id); fa[root]=id; build(root); } } #define mid (l+r)/2 void change(int &id,int l,int r,int tl,int tr,int v) { if(tr==14347&&v==1895) int asfs=0; if(!id) { id=++cnt; //if(cnt>200000) throw 1; } if(l>=tl&&r<=tr) { val[id]+=v; return; } if(tl<=mid) change(lc[id],l,mid,tl,tr,v); if(tr>mid) change(rc[id],mid+1,r,tl,tr,v); } void modify(int id,int dep,int v) { int t=id; change(rt1[t],1,n,1,dep+1,v); while(fa[t]) { int d=get_dis(fa[t],id); if(d>dep) goto jump; change(rt2[t],1,n,1,dep-d+1,v); change(rt1[fa[t]],1,n,1,dep-d+1,v); jump:t=fa[t]; } } int query2(int id,int l,int r,int pos) { if(!id) return 0; if(l==r) return val[id]; if(pos<=mid) return val[id]+query2(lc[id],l,mid,pos); return val[id]+query2(rc[id],mid+1,r,pos); } int query(int id) { int t=id; int ans=query2(rt1[t],1,n,1); while(fa[t]) { int d=get_dis(fa[t],id); ans+=query2(rt1[fa[t]],1,n,d+1)-query2(rt2[t],1,n,d+1); t=fa[t]; } return ans; } signed main() { int m; freopen("4372/1.in","r",stdin); freopen("out.txt","w",stdout); cin>>n>>m; mson[0]=1e9; for(int i=1;i<n;i++) { int a,b; scanf("%d%d",&a,&b); vec[a].push_back(b); vec[b].push_back(a); } get_dep(1,0); sum=n; getroot(1,0); build(root); for(int i=1;i<=m;i++) { char ch; int a,b,c; scanf(" %c%d",&ch,&a); //cout<<ch<<" "<<a; if(ch=='Q') printf("%d\n", query(a));//,cout<<endl; else { scanf("%d%d",&b,&c); // cout<<" "<<b<<" "<<c<<endl; modify(a,b,c); } } }
看都看了,顺手点个推荐呗 :)