[P3806] Divide and Conquer on Tree
Link:
Solution:
询问树上是否存在两点间的距离为$k$,共有$m$次询问($m\le 100,k\le 1e7$)
预处理出所有距离的可能性再$O(1)$出解的复杂度为$O(n^2*log(n))$,明显TLE(但好像并不会)
而如果直接在线处理要分治$m$次,找$m$次完全相同的重心,完全没有必要
因此最好采用离线处理的方式
在每个点运用$set$对于每一个$k$查询$k-dist(i)$在之前的子树中是否出现过
预估复杂度和在线其实没什么区别,都为$O(n*m*log(n)^2)$,但少了$m-1$次递归和求重心常数就小了很多
Code:
#include <bits/stdc++.h> using namespace std; const int MAXN=10005; struct edge{int nxt,to,w;}e[MAXN<<2]; set<int> s; int n,m,x,y,z,st[MAXN],head[MAXN],q[MAXN],res[MAXN],tot,top; int sz[MAXN],mxsub[MAXN],vis[MAXN],vsum,root; void add_edge(int from,int to,int w) { e[++tot].nxt=head[from];e[tot].to=to;e[tot].w=w;head[from]=tot; e[++tot].nxt=head[to];e[tot].to=from;e[tot].w=w;head[to]=tot; } void getroot(int x,int anc) { sz[x]=1;mxsub[x]=0; for(int i=head[x];i;i=e[i].nxt) { if(e[i].to==anc||vis[e[i].to]) continue; getroot(e[i].to,x);sz[x]+=sz[e[i].to]; mxsub[x]=max(mxsub[x],sz[e[i].to]); } mxsub[x]=max(mxsub[x],vsum-sz[x]); if(mxsub[x]<mxsub[root]) root=x; } void dfs(int x,int anc,int dist) { st[++top]=dist; for(int i=head[x];i;i=e[i].nxt) { if(e[i].to==anc||vis[e[i].to]) continue; dfs(e[i].to,x,dist+e[i].w); } } void solve(int x) { vis[x]=true;s.clear();s.insert(0); for(int i=head[x];i;i=e[i].nxt) { if(vis[e[i].to]) continue; top=0;dfs(e[i].to,x,e[i].w); for(int j=1;j<=top;j++)//对m个结果更新 for(int k=1;k<=m;k++) res[k]|=s.count(q[k]-st[j]); for(int j=1;j<=top;j++) s.insert(st[j]); } for(int i=head[x];i;i=e[i].nxt) { if(vis[e[i].to]) continue; vsum=sz[e[i].to];getroot(e[i].to,root=0); solve(root); } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<n;i++) scanf("%d%d%d",&x,&y,&z),add_edge(x,y,z); for(int i=1;i<=m;i++) scanf("%d",&q[i]); vsum=mxsub[0]=n; getroot(1,root=0);solve(root); for(int i=1;i<=m;i++) printf(res[i]?"AYE\n":"NAY\n"); return 0; }
Review:
如果点分治问题有多次询问,最好离线
减少递归和求重心的次数