点分治学习笔记

点分治,能在 \(O(n\log n)\) 复杂度内处理所有树上路径

点分治基于分治思想:每次找到当前联通块的重心,统计经过重心的路径信息,然后对子树递归

重心保证了递归层数不超过 \(\log n\)

P3806 【模板】点分治 1

找重心

inl void getrt(int x,int fa){
    siz[x]=1;maxsiz[x]=0;
    for(int i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==fa||vis[y])continue;
        getrt(y,x);
        siz[x]+=siz[y];
        maxsiz[x]=max(maxsiz[x],siz[y]);
    }
    maxsiz[x]=max(maxsiz[x],s-siz[x]);
    if(maxsiz[x]<maxsiz[rt])rt=x;
}

找去掉自己之后子树大小最大值最小的点 即为重心

求距离

inl void getdis(int x,int fa,int dep){
    siz[x]=1;
    dis.push_back(dep);
    for(int i=head[x];i;i=nxt[i]){
        int y=to[i],c=w[i];
        if(y==fa||vis[y])continue;
        getdis(y,x,dep+c);
        siz[x]+=siz[y];
    }
}

求出子树内所有点到根的距离

求答案

inl void solve(int x){
    del.clear();
    tong[0]=vis[x]=1;del.push_back(0);
    for(int i=head[x];i;i=nxt[i]){
        int y=to[i],c=w[i];
        if(vis[y])continue;
        dis.clear();
        getdis(y,x,c);
        for(auto i:dis){
            for(int j=1;j<=m;j++){
                if(q[j]-i>=0)ans[j]|=tong[q[j]-i];
            }
        }
        for(auto i:dis)tong[i]=1;
        for(auto i:dis)del.push_back(i);
    }
    for(auto i:del)tong[i]=0;
    for(int i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(vis[y])continue;
        s=siz[y];rt=0;
        getrt(y,x);solve(rt);
    }
}

前半部分为 对于每个深度 求对每个询问的贡献 在桶里找到剩下的距离即可
然后对子树找根 再递归下去

关于找重心

一种基于错误的寻找重心方法的点分治的复杂度分析

也就是说 直接把上一次找根求出来的siz当成子树大小做 复杂度也是对的 即使这样显然siz大小是错的

然而求dis时求一遍对的siz 常数显著减小 35ms->21ms 所以还是好好求siz吧(

dsu on tree

似乎dsu on tree更香?

time:24ms

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline 
#define int ll
#define gc getchar
#define pc putchar
const int N=1e6+5;
const int M=1e8+5;
const int inf=0x3f3f3f3f;
const int mod=1e6+3;
inl int read(){
    int x=0,f=1;char c=gc();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=gc();}
    return x*f;
}
inl void write(int x){
    if(x<0){pc('-');x=-x;}
    if(x>9)write(x/10);
    pc(x%10+'0');
}
inl void writei(int x){write(x);pc(' ');}
inl void writel(int x){write(x);pc('\n');}
int n,m,s,rt,dep[N],siz[N],son[N],q[N],ans[N],dfn[N],rev[N],dfs_clock;
bitset<M>tong;
int head[N],nxt[N],to[N],w[N],cnt;
inl void add(int u,int v,int c){
    nxt[++cnt]=head[u];
    to[cnt]=v;
    w[cnt]=c;
    head[u]=cnt;
}
inl void dfs(int x,int fa){
    dfn[x]=++dfs_clock;
    rev[dfs_clock]=x;
    siz[x]=1;
    for(int i=head[x];i;i=nxt[i]){
        int y=to[i],c=w[i];
        if(y==fa)continue;
        dep[y]=dep[x]+c;
        dfs(y,x);
        siz[x]+=siz[y];
        if(siz[y]>siz[son[x]])son[x]=y;
    }
}
inl void solve(int x,int fa,int flag){
    for(int i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==fa||y==son[x])continue;
        solve(y,x,0);
    }
    if(son[x])solve(son[x],x,1);
    int v=dep[x];
    for(int j=1;j<=m;j++){
        if(q[j]+2*dep[x]-v>=0)ans[j]|=tong[q[j]+2*dep[x]-v];
    }
    tong[v]=1;
    for(int i=head[x];i;i=nxt[i]){
        int y=to[i],c=w[i];
        if(y==fa||y==son[x])continue;
        for(int i=dfn[y];i<=dfn[y]+siz[y]-1;i++){
            int v=dep[rev[i]];
            for(int j=1;j<=m;j++){
                if(q[j]+2*dep[x]-v>=0)ans[j]|=tong[q[j]+2*dep[x]-v];
            }
        }
        for(int i=dfn[y];i<=dfn[y]+siz[y]-1;i++)
            tong[dep[rev[i]]]=1;
    }
    if(!flag){
        for(int i=dfn[x];i<=dfn[x]+siz[x]-1;i++){
            tong[dep[rev[i]]]=0;
        }
    }
}
signed main(){
    freopen("1.in","r",stdin);
    n=read();m=read();
    for(int i=1;i<=n-1;i++){
        int u=read(),v=read(),c=read();
        add(u,v,c);add(v,u,c);
    }
    for(int i=1;i<=m;i++)q[i]=read();
    dfs(1,0);solve(1,0,1);
    for(int i=1;i<=m;i++)puts(ans[i]?"AYE":"NAY");
    return 0;
}
posted @ 2023-11-23 09:20  xiang_xiang  阅读(21)  评论(0)    收藏  举报