zoukankan      html  css  js  c++  java
  • [P3806] Divide and Conquer on Tree

    Link:

    P3806 传送门

    Solution:

    询问树上是否存在两点间的距离为$k$,共有$m$次询问($mle 100,kle 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
    ":"NAY
    ");
        return 0;
    }

    Review:

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

    减少递归和求重心的次数

  • 相关阅读:
    2019年春季学期第三周作业
    2019年春季学期第二周作业
    7-2 求最大值及其下标 (20 分)
    7-1 查找整数 (10 分)
    人生中最重要的三位老师
    自我介绍
    学期总结
    编程作业 抓老鼠啊~亏了还是赚了?
    作业——10
    作业——9
  • 原文地址:https://www.cnblogs.com/newera/p/9303145.html
Copyright © 2011-2022 走看看