P3806 【模板】点分治1

P3806 【模板】点分治1

Q:多次询问(可离线)树上距离为k的点对是否存在

A:淀粉质点分治

复杂度:$O(nlogn)$

对于每次分治,我们先找到这棵(子)树的重心$rt$

我们发现,每次询问可分为2种情况。

1.距离为$k$的两个点之间的路径经过$rt$

2.两个点都在这棵(子)树的子树上

每层分治我们都只处理第1种情况

第2种情况就对这棵树的每个子树进行分治以转化为第1种

具体怎么实现呢

我们用$d[i]$表示子树$u_{1}$~$u_{j-1}$与$rt$距离为$i$的点是否存在

每次遍历一个子树$u_{j}$,用$pos$存下每个点与$rt$的距离,然后在$d$中匹配看是否有符合条件的。

再把$pos$存到$d$中

分治结束时记得把$d$清空


$siz[i]$:(子)树$i$的节点数(大小)

$dis[i]$:点$i$与$rt$的距离

$mxd[i]$:最大子树的大小

$pos[i]$:储存遍历子树$j$每个点的距离

$d[i]$:储存子树$1$~$j-1$的是否存在与$rt$距离$i$的点


很丑的code.....

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
inline int max(int &a,int &b){return a>b?a:b;}
void read(int &x){
    char c=getchar();x=0;
    while(c<'0'||c>'9') c=getchar();
    while('0'<=c&&c<='9') x=x*10+(c^48),c=getchar();
}
#define N 10005
#define K 10000005 
int n,m,rt,sum,siz[N],dis[N],mxd[N];
int pos[K],d[K],q[N],ask[105],ok[105];
int cnt,hd[N],nxt[N<<1],ed[N],poi[N<<1],val[N<<1];
bool vis[N];
inline void adde(int x,int y,int v){
    nxt[ed[x]]=++cnt; hd[x]=hd[x]?hd[x]:cnt;
    ed[x]=cnt; poi[cnt]=y; val[cnt]=v;
}
void grt(int x,int fa){//找重心
    siz[x]=1; mxd[x]=0;
    for(int i=hd[x];i;i=nxt[i]){
        int to=poi[i];
        if(to==fa||vis[to]) continue;
        grt(to,x);
        siz[x]+=siz[to];
        mxd[x]=max(mxd[x],siz[to]);
    }mxd[x]=max(mxd[x],sum-siz[x]);
    if(mxd[x]<mxd[rt]) rt=x;
}
void gdis(int x,int fa){//找每个点的距离
    pos[++pos[0]]=dis[x];
    for(int i=hd[x];i;i=nxt[i]){
        int to=poi[i];
        if(to==fa||vis[to]) continue;
        dis[to]=dis[x]+val[i];
        gdis(to,x);
    }
}
void calc(int x){
    q[0]=0; d[0]=1;
    for(int i=hd[x];i;i=nxt[i]){
        int to=poi[i];
        if(vis[to]) continue;
        pos[0]=0; dis[to]=val[i];
        gdis(to,x);
        for(int j=1;j<=pos[0];++j)
            for(int k=1;k<=m;++k)
                if(ask[k]>=pos[j])
                    ok[k]|=d[ask[k]-pos[j]];
        for(int j=1;j<=pos[0];++j)
            q[++q[0]]=pos[j],d[pos[j]]=1;
    }
    for(int i=1;i<=q[0];++i) d[q[i]]=0;//每次只清空用过的空间,memset很慢
}
void solve(int x){
    vis[x]=1; calc(x);
    for(int i=hd[x];i;i=nxt[i]){
        int to=poi[i];
        if(vis[to]) continue;
        rt=0; sum=siz[to];
        grt(to,x); solve(rt);//下一步:每棵子树的重心
    }
}
int main(){
    read(n);read(m);
    for(int i=1,q1,q2,q3;i<n;++i){
        read(q1),read(q2),read(q3);
        adde(q1,q2,q3),adde(q2,q1,q3);
    }
    for(int i=1;i<=m;++i) read(ask[i]);
    mxd[rt=0]=n+1; sum=n; grt(1,0); solve(rt);
    for(int i=1;i<=m;++i) puts(ok[i]?"AYE":"NAY");
    return 0;
}
原文地址:https://www.cnblogs.com/kafuuchino/p/10508088.html