zoukankan      html  css  js  c++  java
  • P3806 点分治【模板】

    给定一颗有n个点的树,询问树上距离为k的点对是否存在

    点分治适合处理大规模的树上路径信息问题。

    我们先随意选择一个节点作为根节点Root

    所有完全位于其子树中的路径可以分为两种

    一种是经过当前根节点的路径

    一种是不经过当前根节点的路径

    对于经过当前根节点的路径,又可以分为两种

    一种是以根节点为一个端点的路径

    一种是两个端点都不为根节点的路径

    而后者又可以由两条属于前者链合并得到

    所以,对于枚举的根节点Root,我们先计算在其子树中且经过该节点的路径对答案的贡献,再递归其子树对不经过该节点的路径进行求解

    在本题中,对于经过根节点Root的路径,先枚举其所有子节点Ch,以Ch为根计算Ch子树中所有节点到Roto的距离。记节点i到当前根节点Root的距离为Dist(i)。

    tf(d)表示之前处理过的子树中是否存在一个节点v使得Dist(v)=d。

    若一个询问的k满足tf(k-Dist(i)) = true,则存在一条长度为k的路径。

    在计算完Ch子树中所连的边能否成为答案后,我们将这些新的距离加入tf数组中。

    注意在清空tf数组的时候不能直接用Memset,而应将之前占用过的Tf位置加入一个队列中,进行清空。这样才能保证时间复杂度。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=2e5+100;
    const int inf=2e9;
    int n,m;
    int q[maxn];
    int rt;
    int size[maxn];
    int Max[maxn];
    int dist[maxn];
    
    bool tf[maxn*100];
    bool ret[maxn];
    bool vis[maxn];
    
    int tot;
    struct node {
        int u,v,w,nxt;
    }edge[maxn<<1];
    int head[maxn];
    void addedge (int u,int v,int w) {
        edge[tot].u=u;
        edge[tot].v=v;
        edge[tot].w=w;
        edge[tot].nxt=head[u];
        head[u]=tot++;
        
        edge[tot].u=v;
        edge[tot].v=u;
        edge[tot].w=w;
        edge[tot].nxt=head[v];
        head[v]=tot++;
    } 
    
    int sum;
    void cal_size (int u,int pre) {
        //计算子树大小
        //计算每个节点的最大子树大小
        size[u]=1;
        Max[u]=0;
        for (int i=head[u];i!=-1;i=edge[i].nxt) {
            int v=edge[i].v;
            if (v==pre) continue;
            if (vis[v]) continue;
            cal_size(v,u);
            Max[u]=max(Max[u],size[v]);
            size[u]+=size[v];
        } 
        Max[u]=max(Max[u],sum-size[u]);
        if (Max[u]<Max[rt]) rt=u; 
    }
    
    int dd[maxn];
    int cnt;
    void cal_dist (int u,int pre) {
        //计算路径
        dd[++cnt]=dist[u];//把每个点距离根节点的路径存到dd里 
        for (int i=head[u];i!=-1;i=edge[i].nxt) {
            int v=edge[i].v;
            if (v==pre) continue;
            if (vis[v]) continue;
            dist[v]=dist[u]+edge[i].w;
            cal_dist(v,u);
        } 
    }
    
    queue<int> tag;
    void Divid (int u,int pre) {
        //分治
        tf[0]=true;
        tag.push(0);
        vis[u]=true;//u已经被分治
        for (int i=head[u];i!=-1;i=edge[i].nxt) {
            int v=edge[i].v;
            if (v==pre) continue;
            if (vis[v]) continue;
            dist[v]=edge[i].w;//v到根节点的路径为w
            cal_dist(v,u);//计算以v为根节点的子树的所有节点到u的距离
            for (int k=1;k<=cnt;k++) for (int j=1;j<=m;j++) {
                if (q[j]>=dd[k]) {
                    //枚举已经记录的所有路径
                    //如果第q次询问大于等于dd[k]
                    //第q次询问的答案或上tf[q[j]-dd[k]]
                    //tf数组表示长度为i的路径是否存在 
                    ret[j]|=tf[q[j]-dd[k]];
                }
            } 
            for (int k=1;k<=cnt;k++) if (dd[k]<maxn*100) {
                tag.push(dd[k]);
                tf[dd[k]]=true;
            }
            cnt=0;
        } 
        while (tag.size()) tf[tag.front()]=false,tag.pop();
        for (int i=head[u];i!=-1;i=edge[i].nxt) {
            int v=edge[i].v;
            if (v==pre) continue;
            if (vis[v]) continue;
            sum=size[v];
            rt=0;
            Max[rt]=inf;
            cal_size(v,u);//先找到以u为根节点的子树的重心 
            cal_size(rt,-1);//以重心为根重新计算子树大小 
            Divid(rt,u);//对u这颗子树进行分治 
        } 
    }
    
    int main () {
        scanf("%d%d",&n,&m);
        for (int i=0;i<=n;i++) head[i]=-1;
        for (int i=1;i<n;i++) {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
        }
        for (int i=1;i<=m;i++) scanf("%d",q+i);
        rt=0;
        Max[rt]=inf;
        sum=n;
        cal_size(1,-1);
        cal_size(rt,-1);
        Divid(rt,-1);
        for (int i=1;i<=m;i++) {
            if (ret[i])
                printf("AYE
    ");
            else
                printf("NAY
    ");
        }
    }
  • 相关阅读:
    C++ 类初始化的顺序
    Given constant integers x and t, write a function that takes no argument and returns true if the function has been called x number of times in last t secs.
    Find out all the elements in A and B such that the A[i]B[j]=C[k]
    顺时针打印数组,美丽版
    Given a string S, find the longest palindromic substring in S.
    很多机器,每台机器上都有很一部分数据,如何输出分布在所有机器上的所有数据的median
    C++'s mutable and conceptual constness
    上20阶楼梯,可以一次迈1,2,3步,请问有多少种上法?
    Count smaller elements on right side in an array.
    一排房子,连续填色,成本最低的问题
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/13863413.html
Copyright © 2011-2022 走看看