码迷,mamicode.com
首页 > 其他好文 > 详细

【BZOJ】2599: [IOI2011]Race 点分治

时间:2018-01-20 12:41:34      阅读:106      评论:0      收藏:0      [点我收藏+]

标签:图片   +=   main   访问   []   isp   题解   分享   src   

【题意】给一棵树,每条边有权.求一条简单路径,权值和等于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

 

【BZOJ】2599: [IOI2011]Race 点分治

标签:图片   +=   main   访问   []   isp   题解   分享   src   

原文地址:https://www.cnblogs.com/onioncyc/p/8320128.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!