点分治学习笔记
点分治是一种对静态树上路径进行统计的一种思想。
它的的流程如下:
- 任意选取一个点为根节点。
- 算出它到当前这棵树所有点的距离\(d_i\)(也可以是其他的信息,视情况而定)。
- 那么对于没有重合路径(这里的路径定义为到根节点的路径)两个点的距离可以直接合并,(比如\(d_x+d_y\))。
- 去掉当前节点,树会分裂成若干个子树,再分治下去对子树做同样的过程。
在随机情况下,会比朴素算法快一点,
但如果是树一条链的话,递归深度达到\(n\)层,依旧很慢。
我们想想,每次应该选择什么点作为当前树的根节点呢?
其实就是树的重心。
因为树的重心有一个性质:以重心为根后大小最大的子树不超过整棵树的一半。
所以至多递归\(logn\)层。
来看几道题目:
POJ1741Tree
对于每一个\(d_x\),统计有多少个\(d_y\)满足\(d_x+d_y\leqslant k\)
移下项:\(d_y \leqslant k - d_x\);
等式右边是个定值,很显然可以用树状数组维护,时间复杂度\(O(nlog^2n)\)
还有种做法是先将所有子树中的\(d_x\)算出来,排序,然后跑个双指针,大家可以自行百度题解。
luoguP3806
你可以使用上题的双指针,也可以使用二分。
鉴于本题值域较小,最简单当然是用个桶了。
hdu4812D-Tree
类似于第一题,此题就是求权值积\(d_y\)满足\(d_x \times d_y \equiv k (mod~1e6+3)\)
显然 \(d_y \equiv k \times inv_{d_x}(mod~1e6+3)\)
预处理一下逆元,然后照着做就好了。。
总结:静态点分治较为简单,用简单的数据结构套个分治模板就行,关键在于发现维护的数据。
#include<bits/stdc++.h>
#define inf (1<<30)
using namespace std;
const int N=2e5+10;
int n,T,cnt,ns,rt;
int pre[N],now[N],to[N],val[N],tot;
int me[N],siz[N],Q[N];
bool vis[N],can[N];
struct Node{
int t,d,bl;
}a[N];
void add(int x,int y,int z){
pre[++tot]=now[x];
now[x]=tot;to[tot]=y;
val[tot]=z;
}
void findrt(int u,int F){
me[u]=0;siz[u]=1;
for(int i=now[u];i;i=pre[i]){
int v=to[i];
if(v==F||vis[v])continue;
findrt(v,u);
siz[u]+=siz[v];
me[u]=max(me[u],siz[v]);
}
me[u]=max(me[u],ns-siz[u]);
if(me[u]<me[rt])rt=u;
}
void DFS(int u,int F,int dis,int col){
++cnt;
a[cnt].t=u;
a[cnt].d=dis;
a[cnt].bl=col;
for(int i=now[u];i;i=pre[i]){
int v=to[i],z=val[i];
if(vis[v]||v==F)continue;
DFS(v,u,dis+z,col);
}
}
bool cmp(Node A,Node B){
if(A.d!=B.d)return A.d<B.d;
}
void solve(int u){
cnt=0;
++cnt;
a[cnt].t=u;
a[cnt].d=0;
a[cnt].bl=u;
for(int i=now[u];i;i=pre[i]){
int v=to[i],z=val[i];
if(vis[v])continue;
DFS(v,u,z,v);
}
sort(a+1,a+cnt+1,cmp);
for(int i=1;i<=T;i++){
int l=1,r=cnt;
while(l<r){
if(a[l].d+a[r].d>Q[i])r--;
else if(a[l].d+a[r].d<Q[i])l++;
else if(a[r].bl==a[l].bl){
if(a[r].bl==a[r-1].bl)r--;
else l++;
}
else{
can[i]=true;
break;
}
}
}
return;
}
void dfs(int u){
vis[u]=true;
solve(u);
for(int i=now[u];i;i=pre[i]){
int v=to[i];
if(vis[v])continue;
ns=siz[v];rt=0;
findrt(v,0);
dfs(rt);
}
}
int main(){
scanf("%d%d",&n,&T);
for(int i=1;i<n;i++){
int u,v,l;
scanf("%d%d%d",&u,&v,&l);
add(u,v,l);add(v,u,l);
}
for(int i=1;i<=T;i++)
scanf("%d",&Q[i]);
ns=n;me[0]=inf;rt=0;
findrt(1,0);
dfs(rt);
for(int i=1;i<=T;i++)
puts(can[i]?"AYE":"NAY");
return 0;
}