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; }