模板题
题目传送门
给定一棵树(带点权),支持以下操作:
修改点权。
查询到一个点距离 \(\le k\) 的点的权值和。
\(n,T\le 10^5\)
算法解析
前置知识:点分治
我们考虑把每次求出的重心和上一层的重心连边,我们就可以得到点分树。
这棵树有以下性质:
- 树高为 \(\log n\),也就是暴力找 LCA 的复杂度、暴力从一个点一路跳到跟的复杂度都是 \(O(\log n)\)
- 所有节点为根的子树的大小和是 \(O(n\log n)\)
- 虽然点分树的边和原树的边没有任何关系,但是对于任意两个点 \(u,v\),在点分树上的两个点的 LCA \(x\) 一定在原树 \(u\) 到 \(v\) 的路径上。这样,对于原树中的路径统计,我们就可以在点分树上通过 \(u\to lca(u,v)\to v\) 来统计。
题目解析
对于每个点,维护一个树状数组来表示在点分树内的子树中到在这个点距离小于等于 \(i\) 的和,显然 \(i\le\) 子树大小,所以空间上可行。
然后考虑求值。我们发现往上跳的时候不能选择同一个子树中的点,所以还需要维护到在这个点父亲距离小于等于 \(i\) 的和用于容斥。
修改直接暴力跳到跟就好。
复杂度 \(O(n\log^2n)\)。(认为 \(n,T\) 同阶)
#include<vector>
#include<cstdio>
#include<iostream>
#include<assert.h>
#define db double
#define pc putchar
#define ll long long
#define Tp template<typename _T>
Tp _T mabs(_T a){ return a<0?-a:a; }
Tp _T mmax(_T a,_T b){ return a<b?b:a; }
Tp _T mmin(_T a,_T b){ return a<b?a:b; }
Tp void mswap(_T &a,_T &b){ _T t=a; a=b; b=t; return; }
using namespace std;
struct IO{
char buf[1<<21],*p1,*p2;
char gc(){ return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++; }
IO & operator >> (char &x){ x=gc(); while(x=='\r'||x=='\n'||x==' ') x=gc(); return *this; }
Tp IO & operator >> (_T &x){
x=0; char c=gc(); bool f=0; while(c<'0'||c>'9'){ if(c=='-') f^=1; c=gc(); }
while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=gc(); f?x=-x:0; return *this;
}
int st[39],top;
IO & operator << (char x) { pc(x); return *this; }
Tp IO & operator << (_T x){
if(x<0) x=-x,pc('-'); if(x==0){ pc('0'); return *this; }
top=0; while(x) st[++top]=x%10,x/=10;
while(top--) pc(st[top+1]+48); return *this;
}
}fin;
#define maxn 100039
int n,lgn,T,opt,u,v,ans,val[maxn],head[maxn],nex[maxn<<1],to[maxn<<1],kkk;
#define add(x,y) nex[++kkk]=head[x]; head[x]=kkk; to[kkk]=y
int dep[maxn],dfn[maxn],f[maxn<<1][22],lg[maxn<<1],cnt;
void dfs(int x,int pre){
int i; dfn[x]=++cnt; f[cnt][0]=x;
for(i=head[x];i;i=nex[i]) if(to[i]!=pre){
dep[to[i]]=dep[x]+1; dfs(to[i],x); f[++cnt][0]=x;
} return;
}
int js(int a,int b){ return dep[a]<dep[b]?a:b; }
int lca(int a,int b){ if(a>b) mswap(a,b); return js(f[a][lg[b-a+1]],f[b-(1<<lg[b-a+1])+1][lg[b-a+1]]); }
int getdis(int a,int b){ return dep[a]+dep[b]-dep[lca(dfn[a],dfn[b])]*2; }
vector<int> c[2][maxn];
int sum,root,minx,fa[maxn],siz[maxn],mark[maxn];
void getsiz(int x,int pre){
sum++; int i; for(i=head[x];i;i=nex[i]) if(to[i]!=pre&&!mark[to[i]]) getsiz(to[i],x); return;
}
void getroot(int x,int pre){
int i,maxx=-1; siz[x]=1; for(i=head[x];i;i=nex[i]) if(to[i]!=pre&&!mark[to[i]]){
getroot(to[i],x); siz[x]+=siz[to[i]]; if(siz[to[i]]>maxx) maxx=siz[to[i]];
} maxx=mmax(maxx,sum-siz[x]); if(maxx<minx) minx=maxx,root=x; return;
}
int work(int x){
sum=0; getsiz(x,-1); minx=10000000; getroot(x,-1); int i,tmp=root;
c[0][root].resize(sum+10),c[1][root].resize(sum+10);
mark[root]=1; for(i=head[root];i;i=nex[i]) if(!mark[to[i]]) fa[work(to[i])]=tmp;
return tmp;
}
#define lowbit(x) (x&-x)
void upd(int x,int y,int z,int w){
z++; int t=c[x][y].size(); while(z<=t){ c[x][y][z-1]+=w; z+=lowbit(z); } return;
}
int que(int x,int y,int z){
z++; z=mmin(z,(signed)c[x][y].size()); int s=0;
while(z){ s+=c[x][y][z-1]; z-=lowbit(z); } return s;
}
void update(int x,int y){
int i;
for(i=x;i;i=fa[i])
upd(0,i,getdis(x,i),y);
for(i=x;fa[i];i=fa[i])
upd(1,i,getdis(x,fa[i]),y);
}
void query(int x,int k){
ans=0; int i,tmp; ans+=que(0,x,k);
for(i=x;fa[i];i=fa[i]){
tmp=getdis(x,fa[i]);
if(k>=tmp) ans+=que(0,fa[i],k-tmp)-que(1,i,k-tmp);
} return;
}
int main(){
freopen("1.in","r",stdin);
//freopen(".out","w",stdout);
fin>>n>>T; int i,j; for(i=1;i<=n;i++) fin>>val[i];
for(i=1;i<n;i++){ fin>>u>>v; add(u,v); add(v,u); }
cnt=0; dfs(1,-1); for(i=1;i<=cnt;i++){ lg[i]=lg[i-1]; if((1<<(lg[i]+1))<=i) lg[i]++; }
lgn=lg[cnt];
for(j=1;j<=lgn;j++) for(i=1;i<=cnt;i++)
if(i+(1<<j)-1<=cnt) f[i][j]=js(f[i][j-1],f[i+(1<<(j-1))][j-1]);
work(1); for(i=1;i<=n;i++) update(i,val[i]);
while(T--){
fin>>opt>>u>>v; u^=ans,v^=ans; assert(1<=u&&u<=n);
if(opt) update(u,v-val[u]),val[u]=v;
else query(u,v),fin<<ans<<'\n';
}
return 0;
}
标签:maxx,return,int,siz,分治,笔记,gc,点分树,maxn
From: https://www.cnblogs.com/jiangtaizhe001/p/17644587.html