zoukankan      html  css  js  c++  java
  • 【BZOJ】2599: [IOI2011]Race 点分治

    【题意】给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 1000000。注意点从0开始编号,无解输出-1。

    【算法】点分治

    【题解】每个区域按重心x划分子树,一条经过x的路径是从一个子树到另一个子树的(从x出发记为深度0即可),那么就让子树i的路径与子树1~i-1尝试合并,然后并入,这样就可以找到所有路径。

    每个区域记录t[i]表示长度为i的路径最小边数,就可以过程中合并时计算答案ans = min{ ans , deep[x]+t[k-dis[x]] } 。

    每个区域处理完要把t[]清空,然后再进入各个子区域。清空不能memset(否则复杂度不对),只能清空访问过的点。

    复杂度O(n log n)。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn=200010,maxk=1000010;
    int first[maxn],tot,sz[maxn],n,root,k,sum,dis[maxn],deep[maxn],ans,t[maxk],c[maxn],L,R;
    bool vis[maxn];
    struct edge{int v,w,from;}e[maxn*2];
    void insert(int u,int v,int w){tot++;e[tot].v=v;e[tot].w=w;e[tot].from=first[u];first[u]=tot;}
    void getroot(int x,int fa){
        sz[x]=1;bool ok=1;
        for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa&&!vis[e[i].v]){
            getroot(e[i].v,x);
            sz[x]+=sz[e[i].v];
            if(sz[e[i].v]>sum/2)ok=0;
        }
        if(sum-sz[x]<=sum/2&&ok)root=x;
    }
    void dfs(int x,int fa){
        if(dis[x]>k)return;
        ans=min(ans,deep[x]+t[k-dis[x]]);
        c[++R]=x;
        for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa&&!vis[e[i].v]){
            dis[e[i].v]=dis[x]+e[i].w;
            deep[e[i].v]=deep[x]+1;
            dfs(e[i].v,x);
        }
    }
    void solve(int x,int s){
        vis[x]=1;L=0;R=0;
        for(int i=first[x];i;i=e[i].from)if(!vis[e[i].v]){
            deep[e[i].v]=1;dis[e[i].v]=e[i].w;
            dfs(e[i].v,x);
            for(int j=L+1;j<=R;j++)t[dis[c[j]]]=min(t[dis[c[j]]],deep[c[j]]);
            L=R;
        }
        for(int i=1;i<=R;i++)t[dis[c[i]]]=n;
        for(int i=first[x];i;i=e[i].from)if(!vis[e[i].v]){
            if(sz[e[i].v]>sz[x])sum=s-sz[x];else sum=sz[e[i].v];
            root=e[i].v;
            getroot(e[i].v,x);
            solve(root,sum);
        }
    }
    int main(){
        scanf("%d%d",&n,&k);
        int u,v,w;
        for(int i=1;i<n;i++){
            scanf("%d%d%d",&u,&v,&w);
            u++;v++;
            insert(u,v,w);insert(v,u,w);
        }
        root=1;sum=n;ans=n;
        for(int i=1;i<=k;i++)t[i]=n;
        getroot(1,0);
        solve(root,n);
        if(ans==n)printf("-1");
        else printf("%d",ans);
        return 0;
    }
    View Code
  • 相关阅读:
    CPU和Memory压力测试方法
    WIN 系统怎么样查看EXpressCache功能
    OGG 源端与目标端 约束不一致
    oracle 查询角色具有的权限
    Mysql 主从一致校验工具------Maatkit工具包
    从库找不到对应的被删除的记录
    python安装包是出现错误解决
    mysql5.7用户密码策略问题
    Centos7上安装docker
    SQL通过身份证获取信息
  • 原文地址:https://www.cnblogs.com/onioncyc/p/8320128.html
Copyright © 2011-2022 走看看