zoukankan      html  css  js  c++  java
  • hdu6881(求删除尽可能小的点使得树的直径不超过K,统计距离点长度大于k/2的点数,距离边长度大于k/2+1的点数)

    题:http://acm.hdu.edu.cn/showproblem.php?pid=6881

    题意:给定n个节点的树,问删除尽可能小的点使得树的直径不超过K,输出最小删除的点数,(1<=k<=n<=3e5)

    分析:

       核心:枚举中间点或中间边;

       枚举中间点的情况是k是偶数,因为直径长可被“劈”成均等的俩半,枚举中间边的情况是k为奇数;

       考虑树分治,每次找重心去考虑;

       k是偶数的情况:点u作为直径中心点,那么距离u长度大于k/2的点数均要删去;

                那么就每次取重心,重心和孩子分别来考虑;

                统计不在子树u的点子树u的每一个节点造成的贡献;

                通过先求总的深度计数cnt[],和当前子树u的深度计数cnt2[];

                做差就能求出其他子树对自己的影响而不会统计到自己对自己;

       k为奇数的情况:边作为直径中心边,距离当前边长度大于k/2+1的点数均要删去;

                边和点不同,所以当枚举的边是当前重心root时,还要加上自己子树长度大于k/2+1的点数。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    using namespace std;
    const int inf=0x3f3f3f3f;
    const int M=3e5+5;
    struct node{
        int v,nextt;
    }e[M<<1];
    int T,n,K,tot,sum,root;
    int head[M],f[M],vis[M],sz[M],cnt[M],cnt1[M],res[M],num1,num2;
    void init(){
        tot=1;
        for(int i=1;i<=n;i++)
            vis[i]=head[i]=f[i]=res[i]=cnt[i]=sz[i]=0;
    }
    void addedge(int u,int v){
        tot++;
        e[tot].v=v;
        e[tot].nextt=head[u];
        head[u]=tot;
    }
    void find_root(int u,int fa){///找重心
        sz[u]=1;
        f[u]=0;
        for(int i=head[u];i;i=e[i].nextt){
            int v=e[i].v;
            if(v!=fa&&!vis[v]){
                find_root(v,u);
                f[u]=max(f[u],sz[v]);
                sz[u]+=sz[v];
            }
        }
        f[u]=max(f[u],sum-sz[u]);
        if(f[u]<f[root]) root = u;
    }
    
    void dfs1(int u,int fa,int dep){///计算cnt1[]
        cnt[dep]++;
        num1=max(num1,dep);
        for(int i=head[u];i;i=e[i].nextt){
            int v=e[i].v;
            if(!vis[v]&&v!=fa) dfs1(v,u,dep+1);
        }
    }
    
    void dfs2(int u,int fa,int dep){
        cnt1[dep]++;
        num2=max(num2,dep);
        for(int i=head[u];i;i=e[i].nextt){
            int v=e[i].v;
            if(v!=fa&&!vis[v]) dfs2(v,u,dep+1);
        }
    }
    
    void dfs3(int u,int fa,int dep,int curedge){
        if(K&1){
            int D=max(0,(K/2+1)-(dep-1));///距离这条边长度大于等于(K/2+1)的个数,就是是长度大于K/2的个数
            res[curedge]+=cnt[D]-cnt1[D];
            if(fa==root)///当前边是和root连的边时,要算子树u中对当前边的贡献,
                res[curedge]+=cnt1[(K/2+1)+1];///之所以后者要+1,时是因为要从当前边开始往下找,而当前边深度对于root来说一定是1;
        }
        else{
            int D=max(0,K/2+1-dep);
            res[u]+=cnt[D]-cnt1[D];
        }
        for(int i=head[u];i;i=e[i].nextt){
            int v=e[i].v;
            if(!vis[v]&&v!=fa)
                dfs3(v,u,dep+1,i/2);
        }
    }
    void solve(int u){
        vis[u]=1;
        num1=0;
        cnt[0]=1;///总的
        for(int i=head[u];i;i=e[i].nextt){
            int v=e[i].v;
            if(!vis[v]) dfs1(v,u,1);
        }
        for(int i=num1-1;i>=0;i--) cnt[i]+=cnt[i+1];
        if(K%2==0) res[u]+=cnt[K/2+1];///当前重心与它的孩子分开算
    
        for(int i=head[u];i;i=e[i].nextt){
            int v=e[i].v;
            if(!vis[v]){
                num2=0;
                ///深度为0的点固然存在,所以不用做cnt1[0]=1的操作
                dfs2(v,u,1);
                for(int j=num2-1;j>=0;j--) cnt1[j]+=cnt1[j+1];
                dfs3(v,u,1,i/2);/// i/2为边的编号
                for(int j=num2;j>=0;j--) cnt1[j]=0;
            }
        }
        for(int i=num1;i>=0;i--) cnt[i]=0;
        for(int i=head[u];i;i=e[i].nextt){
            int v=e[i].v;
            if(!vis[v]){
                sum=sz[v];
                root=0;
                find_root(v,u);
                solve(root);
            }
        }
    }
    int main(){
        int T;
        scanf("%d",&T);
        while(T--){
            scanf("%d%d",&n,&K);
            init();
            for(int u,v,i=1;i<n;i++){
                scanf("%d%d",&u,&v);
                addedge(u,v);
                addedge(v,u);
            }
            f[0]=inf;
            root=0;
            sum=n;
            find_root(1,0);
            solve(root);
            int ans=inf;
            for(int i=1;i<n;i++)
                ans=min(ans,res[i]);
            if(K%2==0)///偶数看点,奇数看边,而边只有n-1条
                ans=min(ans,res[n]);
            printf("%d
    ",ans);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    第八章:Junit—— Eclipse 中 Junit 中的 webDriver 解压版
    第八章:Junit—— Eclipse 中 Junit 中的 webDriver 完整版
    第八章:Junit——在 Eclipse 中里面 导入 Junit 的 jar 包
    20135203齐岳信息安全系统设计基础——实验三实验报告
    20135203齐岳 信息安全系统设计基础第十二周实践总结
    20135203齐岳信息安全系统设计基础——实验二实验报告
    20135203齐岳 信息安全系统设计基础第十一周学习总结
    20135203齐岳信息安全系统设计基础——实验一实验报告
    20135203齐岳 信息安全系统设计基础第十周学习总结
    20135203齐岳 信息安全系统设计基础第九周学习总结
  • 原文地址:https://www.cnblogs.com/starve/p/13781763.html
Copyright © 2011-2022 走看看