点分树
关于模拟赛 \(T2\) 考点分树这件事。
点分治
点分树被称为动态点分治,所以下面先介绍点分治。
P3806 【模板】点分治 1
点分治板子。考虑一个树上的路径,如果已知所有 \(dis,\) 可以按是否经过根划分为:
- 经过根:\(dis[u]+dis[v],\) 这个方便用桶处理。
- 没有经过根:经过了根的子树的根,分治处理根的子树。这也就是点分治。
所以只需要慢慢分治就好了。但是你会发现这样做的复杂度是 \(\mathcal{O(n^2)}\) 的,不可接受。所以考虑优化。
注意到每次的递归数量取决于子树大小,为了让每个子树都很小,可以考虑将重心作为子树的根。
每一次分治的时候分治子树重心就好了。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+10,inf=1e7,maxn=1e7+10;
int edg,fir[N],nxt[N],to[N],w[N],que[N],sz[N],maxi[N],dis[N],ans[N];
int n,m,rt,top,cnt,sum,stk[N],dd[N];
bitset<maxn>vis,tf;
inline void add(int x,int y,int z){ nxt[++edg]=fir[x],fir[x]=edg,to[edg]=y,w[edg]=z; }
void dfs1(int u,int f){
sz[u]=1,maxi[u]=0;
for(int t=fir[u];t;t=nxt[t]){
int v=to[t];
if(v^f&&!vis[v])dfs1(v,u),sz[u]+=sz[v],maxi[u]=max(maxi[u],sz[v]);
}
maxi[u]=max(maxi[u],sum-sz[u]);
rt=(maxi[u]<maxi[rt]?u:rt);
}
void dfs2(int u,int f){
dd[++cnt]=dis[u];
for(int t=fir[u];t;t=nxt[t]){
int v=to[t];
if(v^f&&!vis[v])dis[v]=dis[u]+w[t],dfs2(v,u);
}
return;
}
void dfz(int u,int f){
vis[u]=true,stk[++top]=0,tf[0]=true;
for(int t=fir[u];t;t=nxt[t]){
int v=to[t];
if(v^f&&!vis[v]){
dis[v]=w[t];
dfs2(v,u);
dis[v]=w[t],dfs2(v,u);
for(int k=1;k<=cnt;++k)for(int i=1;i<=m;++i)if(que[i]>=dd[k])ans[i]|=tf[que[i]-dd[k]];
for(int k=1;k<=cnt;++k)if(dd[k]<inf)stk[++top]=dd[k],tf[dd[k]]=true;
cnt=0;
}
}
while(top)tf[stk[top--]]=0;
for(int t=fir[u];t;t=nxt[t]){
int v=to[t];
if(!vis[v]&&v^f)sum=sz[v],rt=0,maxi[rt]=inf,dfs1(v,u),dfz(rt,u);
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1,x,y,z;i<n;++i)cin>>x>>y>>z,add(x,y,z),add(y,x,z);
for(int i=1;i<=m;++i)cin>>que[i];
maxi[rt]=inf,sum=n,rt=0,dfs1(1,-1),dfz(rt,-1);
for(int i=1;i<=m;++i)if(ans[i])cout<<"AYE\n";else cout<<"NAY\n";
return 0;
}
P4178 Tree
和上一道题大致相同,注意到复杂度瓶颈在于统计 \(tf.\) 考虑到 \(tf\) 的计算区间是连续的 ,使用一个树状数组维护之。
标签:rt,sz,edg,int,分治,maxi From: https://www.cnblogs.com/chihirofujisaki/p/18457864/dfz