动态点分治裸题
首先介绍一下动态点分治:就是带修改操作的点分治(废话)
操作涉及单点查询和区间修改
那么首先应该想到线段树
但是怎么操作呢?
首先,修改一个点时的影响范围可以分为上下两部分:一部分在自己的子树内,另一部分通过自己的父节点传上去或传到其他子树内
那么为了使这种操作复杂度尽可能低,我们用点分治优化,因为对于一棵点分树而言,它的深度是对数级的
所谓点分树,就是利用点分治的思想,每次拎起一棵子树的重心作为根,这样重构出的树就叫点分数,它的深度均摊对数级别
那么我们首先建起一棵点分树,在每次修改时,直接在对应线段树上处理即可。
我们把区间修改,单点查询变为单点修改,区间查询
具体来讲,我们对每个点开一棵线段树,每个位置代表到这个点的距离,然后变区间修改为单点修改,向上递归(这个做法就是找到在每次修改过程中,一个点能影响的范围有多大,要求子节点受祖宗节点的影响,那么我们在线段树上对最大的影响范围的位置进行修改,然后区间查询查影响范围合法的部分累计答案,而在递归过程中,对于同一次修改,每个点的影响范围是不一样的,因为还要去掉真正被修改的点到这个点的距离,这会导致一个点影响范围变小)
但是这里有个问题:会有重复!
所以我们需要做一个容斥:
对每个点再开一棵线段树,用于记录重复的部分。在每次修改递归时,记录上一个递归到的位置,然后把那个位置对应的线段树上的相应位置同样打标记修改即可
为什么要这么做?
当我们反复向上递归修改时,很有可能产生一个问题:一个节点的贡献被反复计算!
究其原因,就是这个点已经被修改一遍了,而在递归过程中去修改祖宗节点时,由于祖宗节点对子节点会产生影响,所以将来统计答案时可能将同一个答案统计多次
这显然是不合理的
所以我们对每个点再开一棵线段树,去维护多余的部分,在最后查询时减掉这一部分就好了
而最后查询就是做一个区间查询,对于每个被查询的点,我们向上查出它的所有祖宗节点,而祖宗节点对这个节点的影响就是影响区域大于等于祖宗节点到这个节点距离的贡献之和
注意范围(也就是线段树的端点下标)从0开始
#include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <queue> #include <stack> using namespace std; const int inf=0x3f3f3f3f; struct Edge { int next; int to; }edge[200005]; struct Segtree { int tot; int rot[100005]; int lson[15000005]; int rson[15000005]; int v[15000005]; void update(int &rt,int lq,int rq,int qx,int w) { if(!rt)rt=++tot; v[rt]+=w; if(lq==rq)return; int mid=(lq+rq)>>1; if(qx<=mid)update(lson[rt],lq,mid,qx,w); else update(rson[rt],mid+1,rq,qx,w); } int query(int rt,int lq,int rq,int ql,int qr) { if(!rt)return 0; if(lq>=ql&&rq<=qr)return v[rt]; int mid=(lq+rq)>>1; int ret=0; if(ql<=mid)ret+=query(lson[rt],lq,mid,ql,qr); if(qr>mid)ret+=query(rson[rt],mid+1,rq,ql,qr); return ret; } }tree1,tree2; int head[100005]; int f[100005][30]; int dep[100005]; int pre[100005]; int siz[100005]; int maxp[100005]; bool vis[100005]; char ch[2]; int s,rt; int n,m; int cnt=1; void init() { memset(head,-1,sizeof(head)); cnt=1; } void add(int l,int r) { edge[cnt].next=head[l]; edge[cnt].to=r; head[l]=cnt++; } void dfs(int x,int fx) { f[x][0]=fx; dep[x]=dep[fx]+1; for(int i=head[x];i!=-1;i=edge[i].next) { int to=edge[i].to; if(to==fx)continue; dfs(to,x); } } void getf() { for(int i=1;i<=25;i++) { for(int j=1;j<=n;j++)f[j][i]=f[f[j][i-1]][i-1]; } } int LCA(int x,int y) { if(dep[x]>dep[y])swap(x,y); for(int i=25;i>=0;i--)if(dep[f[y][i]]>=dep[x])y=f[y][i]; if(x==y)return x; int ret; for(int i=25;i>=0;i--) { if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; else ret=f[x][i]; } return ret; } int get_dis(int x,int y) { return dep[x]+dep[y]-2*dep[LCA(x,y)]; } void get_rt(int x,int fa) { siz[x]=1,maxp[x]=0; for(int i=head[x];i!=-1;i=edge[i].next) { int to=edge[i].to; if(vis[to]||to==fa)continue; get_rt(to,x); siz[x]+=siz[to],maxp[x]=max(maxp[x],siz[to]); } maxp[x]=max(maxp[x],s-siz[x]); if(maxp[x]<maxp[rt])rt=x; } void solve(int x) { vis[x]=1; for(int i=head[x];i!=-1;i=edge[i].next) { int to=edge[i].to; if(vis[to])continue; rt=0,s=siz[to],maxp[rt]=inf; get_rt(to,0); pre[rt]=x; solve(rt); } } int main() { scanf("%d%d",&n,&m); init(); for(int i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y); add(x,y),add(y,x); } dfs(1,1); getf(); maxp[rt]=s=n; get_rt(1,0); solve(rt); while(m--) { scanf("%s",ch); if(ch[0]=='M') { int x,k,w; scanf("%d%d%d",&x,&k,&w); int p=x,las=0; while(p) { int d=k-get_dis(x,p); if(d>=0) { tree1.update(tree1.rot[p],0,n,min(n,d),w); if(las)tree2.update(tree2.rot[las],0,n,min(n,d),w); } las=p,p=pre[p]; } }else { int x; scanf("%d",&x); int ans=0,p=x; while(p) { ans+=tree1.query(tree1.rot[p],0,n,get_dis(p,x),n); if(pre[p])ans-=tree2.query(tree2.rot[p],0,n,get_dis(pre[p],x),n); p=pre[p]; } printf("%d ",ans); } } return 0; }