[P3806] Divide and Conquer on Tree

Link:

P3806 传送门

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:

如果点分治问题有多次询问,最好离线

减少递归和求重心的次数

 

posted @ 2018-07-13 09:12  NewErA  阅读(204)  评论(0编辑  收藏  举报