点分治学习笔记
点分治是一种对静态树上路径进行统计的一种思想。
它的的流程如下:
- 任意选取一个点为根节点。
- 算出它到当前这棵树所有点的距离di(也可以是其他的信息,视情况而定)。
- 那么对于没有重合路径(这里的路径定义为到根节点的路径)两个点的距离可以直接合并,(比如dx+dy)。
- 去掉当前节点,树会分裂成若干个子树,再分治下去对子树做同样的过程。
在随机情况下,会比朴素算法快一点,
但如果是树一条链的话,递归深度达到n层,依旧很慢。
我们想想,每次应该选择什么点作为当前树的根节点呢?
其实就是树的重心。
因为树的重心有一个性质:以重心为根后大小最大的子树不超过整棵树的一半。
所以至多递归logn层。
来看几道题目:
POJ1741Tree
对于每一个dx,统计有多少个dy满足dx+dy⩽k
移下项:dy⩽k−dx;
等式右边是个定值,很显然可以用树状数组维护,时间复杂度O(nlog2n)
还有种做法是先将所有子树中的dx算出来,排序,然后跑个双指针,大家可以自行百度题解。
luoguP3806
你可以使用上题的双指针,也可以使用二分。
鉴于本题值域较小,最简单当然是用个桶了。
hdu4812D-Tree
类似于第一题,此题就是求权值积dy满足dx×dy≡k(mod 1e6+3)
显然 dy≡k×invdx(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;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· .NET 适配 HarmonyOS 进展
· .NET 进程 stackoverflow异常后,还可以接收 TCP 连接请求吗?
· 本地部署 DeepSeek:小白也能轻松搞定!
· 基于DeepSeek R1 满血版大模型的个人知识库,回答都源自对你专属文件的深度学习。
· 在缓慢中沉淀,在挑战中重生!2024个人总结!
· 大人,时代变了! 赶快把自有业务的本地AI“模型”训练起来!
· Tinyfox 简易教程-1:Hello World!