【学习笔记】数据结构——点分治
点分治
P3806
点分治可以将 \(O(n^2)\) 优化成 \(O(n \times \log n)\)
核心操作
每次把树存入,找出重心作为 \(root\),动态遍历子树每个点到 \(root\) 距离
再判断前面的子树是否可以与当前子树结合 \(=k\) 。给 \(root\) 打上重心标记记作删除,然后回退清空
复杂度
共 \(log\) 层,每层遍历整张图,复杂度 \(O(nlogn)\)
代码
#include<bits/stdc++.h>
#define Pair pair<int,int>
#define w first
#define to second
using namespace std;
const int N=1e4+10,Q=1e7+10;int n,m;
bool aaa;
vector<Pair> mp[N];
int ask[N];int ans[N];
int cnt;
int del[N];//是否被删除
int siz[N];
int mson[N];
void fsiz(int u,int fa){
cnt++;
siz[u]=1;
for(auto e:mp[u]){
int v=e.to;
if(v==fa||del[v]) continue;
fsiz(v,u);
siz[u]+=siz[v];
}
}
int root;
void Root(int u,int fa){
mson[u]=cnt-siz[u];
for(auto e:mp[u]){
int v=e.to;
if(v==fa||del[v]) continue;
Root(v,u);
mson[u]=max(mson[u],siz[v]);
}
if(root==-1||mson[u]<mson[root]) root=u;
}
bool hav[Q];//数值是否存在
vector<int> nown;
void get(int u,int fa,int dis){//计算当前子树数值
nown.push_back(dis);
for(auto e:mp[u]){
int v=e.to,w=e.w;
if(v==fa||del[v]) continue;
get(v,u,dis+w);
}
}
void solve(int u){
//获取重心
cnt=0;fsiz(u,u);
root=-1;Root(u,u)
//子树处理
hav[0]=1;
u=root;
for(auto e:mp[u]){
int v=e.to,w=e.w;
if(del[v]) continue;
int beg=nown.size();
get(v,u,w);
for(int i=1;i<=m;i++){
for(int j=beg;j<nown.size()&&(!ans[i]);j++){
if(ask[i]>=nown[j]) ans[i]|=hav[ask[i]-nown[j]];
}
}
for(int i=beg;i<nown.size();i++){
if(nown[i]<Q) hav[nown[i]]=1;
}
}
//回退
while(nown.size()){
if(nown.back()<Q)hav[nown.back()]=0;
nown.pop_back();
}
del[u]=1;
for(auto e:mp[u]){
int v=e.to;
if(del[v]) continue;
solve(v);
}
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<n;i++){
int a,b,c;cin>>a>>b>>c;
mp[a].push_back({c,b});
mp[b].push_back({c,a});
}
for(int i=1;i<=m;i++) cin>>ask[i];
solve(1);
for(int i=1;i<=m;i++) cout<<(ans[i]?"AYE\n":"NAY\n");
return 0;
}
代码易错点(警示后人)
-
第15行,\(cnt++\)
-
第51行,\(u=root\)
点分树
分析
把点分治所搜出的重心构建成一棵树,称之为点分树
点分树上 \(x,y\) 的 \(lca\),原树中 \(lca\) 一定在 \(x->y\) 的路径上
所以对于每个点 \(u\) 开两颗线段树,一颗表示子树中在原树上与 \(u\) 的距离为 \(k\) 的权值和,一颗表示子树中在原树上与 \(fa[u]\) 的距离为 \(k\) 的权值和
查询的时候向上跳,累加 \(f_{fa[u]}-g_u\) 即可
更新的时候向上跳,直接更新线段树
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,logN=log2(N)+1;int n;
int nw[N];
vector<int> mp[N];
int st[logN][N];
int dep[N];
//求原树距离
void dfs(int u,int pa){
st[0][u]=pa;
dep[u]=dep[pa]+1;
for(auto v:mp[u]){
if(v==pa) continue;
dfs(v,u);
}
}
int LCA(int a,int b){
if(dep[a]<dep[b]) swap(a,b);
int x=dep[a]-dep[b];
for(int i=0;x>>i;i++){
if(x>>i&1) a=st[i][a];
}
if(a==b) return a;
for(int i=logN-1;i>=0;i--){
if(st[i][a]!=st[i][b]){
a=st[i][a];
b=st[i][b];
}
}
return st[0][a];
}
int dis(int x,int y){
return dep[x]+dep[y]-(dep[LCA(x,y)]<<1);
}
//求原树距离
int cnt,root;
int del[N];
int siz[N];
//构建点分树
void Size(int u,int pa){
cnt++;
siz[u]=1;
for(auto v:mp[u]){
if(v==pa||del[v]) continue;
Size(v,u);
siz[u]+=siz[v];
}
}
int mson[N];
void Root(int u,int pa){
mson[u]=cnt-siz[u];
for(auto v:mp[u]){
if(v==pa||del[v]) continue;
mson[u]=max(mson[u],siz[v]);
Root(v,u);
}
if(root==-1||mson[u]<mson[root]) root=u;
}
int fa[N];
void calc(int u,int pa){
//cerr<<u<<"\n";
cnt=0;Size(u,u);
root=-1;Root(u,u);
u=root;
fa[u]=pa;
del[u]=1;
for(auto v:mp[u]){
if(del[v]||v==pa) continue;
calc(v,u);
}
}
//构建点分树
//对于每个节点维护线段树
struct segtree{
int rt[N],idx=0;
struct node{
int sum;
int ls,rs;
#define sum(q) tree[q].sum
#define ls(q) tree[q].ls
#define rs(q) tree[q].rs
}tree[(int)5e6];
void push_up(int q){
sum(q)=sum(ls(q))+sum(rs(q));
}
void update(int &q,int l,int r,int tp,int d){
//cerr<<q<<"\n";
if(!q){
q=++idx;
}
if(l==r){
sum(q)+=d;
return ;
}
int mid=(l+r)>>1;
if(tp<=mid) update(ls(q),l,mid,tp,d);
else update(rs(q),mid+1,r,tp,d);
push_up(q);
}
int query(int q,int l,int r,int L,int R){
if(!q) return 0;
if(L<=l&&r<=R){
return sum(q);
}
int mid=(l+r)>>1;
int res=0;
if(L<=mid) res+=query(ls(q),l,mid,L,R);
if(mid<R) res+=query(rs(q),mid+1,r,L,R);
return res;
}
}w1,w2;
//对于每个节点维护线段树
//更新/查询
void update(int x,int v){
int now=x;
while(now){
w1.update(w1.rt[now],0,n-1,dis(now,x),v);
if(fa[now]) w2.update(w2.rt[now],0,n-1,dis(fa[now],x),v);
now=fa[now];
}
}
int query(int x,int k){
int ans=0;
int now=x,son=0;
while(now){
if(dis(now,x)>k){
son=now;now=fa[now];continue;
}
ans+=w1.query(w1.rt[now],0,n-1,0,k-dis(x,now));
if(son) ans-=w2.query(w2.rt[son],0,n-1,0,k-dis(x,now));
son=now;now=fa[now];
}
return ans;
}
//更新/查询
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
//freopen("P6329_2.in","r",stdin);
//freopen("std.out","w",stdout);
int m;cin>>n>>m;
for(int i=1;i<=n;i++) cin>>nw[i];
for(int i=1;i<n;i++){
int a,b;cin>>a>>b;
mp[a].push_back(b);
mp[b].push_back(a);
}
dfs(1,0);
for(int k=1;k<logN;k++){
for(int u=1;u<=n;u++) st[k][u]=st[k-1][st[k-1][u]];
}
calc(1,0);
for(int i=1;i<=n;i++) update(i,nw[i]);
int ans=0;
while(m--){
int op,x,y;cin>>op>>x>>y;
x^=ans;y^=ans;
if(op==0){
ans=query(x,y);
cout<<ans<<"\n";
}
else{
update(x,y-nw[x]);
nw[x]=y;
}
}
//cerr<<double(clock())/CLOCKS_PER_SEC<<"\n";
return 0;
}

浙公网安备 33010602011771号