【模板】IOI2011race-点分治

题意

给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小
N200000,K1000000 N ≤ 200000 , K ≤ 1000000


代码

点分治的一类经典模型

#include<cstdio>
#include<algorithm>
#include<cctype>
using namespace std;
const int N=2e5+10,inf=2e9,K=1e6+10;
int n,k,ans=inf,sz[N],S,MX,rt,vis[N],mx[N],ct[K];
int head[N],to[N<<2],nxt[N<<2],w[N<<2],tot,dis[N],d[N];

inline int rd()
{
    char ch=getchar();int x=0,f=1;
    while(!isdigit(ch)){if(ch==-'-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
    return x*f;
}

inline void lk(int u,int v,int c)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;w[tot]=c;}

inline void getrt(int x,int fa)
{
    register int i,j;
    sz[x]=1;mx[x]=0;
    for(i=head[x];i;i=nxt[i]){
        j=to[i];if(j==fa || vis[j]) continue;
        getrt(j,x);
        sz[x]+=sz[j];
        mx[x]=max(mx[x],sz[j]);
    }
    mx[x]=max(mx[x],S-sz[x]);
    if(mx[x]<MX){rt=x;MX=mx[x];}
}

inline void cal(int x,int fa)
{
    register int i,j;
    if(dis[x]<=k) ans=min(ans,ct[k-dis[x]]+d[x]);
    for(i=head[x];i;i=nxt[i]){
        j=to[i];
        if(j==fa || vis[j]) continue;
        d[j]=d[x]+1;dis[j]=dis[x]+w[i];
        cal(j,x);
    }
}

inline void update(int x,int fa)
{
    if(dis[x]<=k) ct[dis[x]]=min(ct[dis[x]],d[x]);
    for(register int j,i=head[x];i;i=nxt[i]){
        j=to[i];if(vis[j] || j==fa)continue;
        update(j,x);
    }
}

inline void del(int x,int fa)
{
    if(dis[x]<=k) ct[dis[x]]=inf;
    for(register int j,i=head[x];i;i=nxt[i]){
        j=to[i];if(vis[j] || j==fa)continue;
        del(j,x);
    }
}

inline void divide(int x)
{
    register int i,j;
    vis[x]=1;ct[0]=0;
    for(i=head[x];i;i=nxt[i]){
        j=to[i];
        if(vis[j]) continue;
        dis[j]=w[i];d[j]=1;
        cal(j,x);update(j,x);
    }
    for(i=head[x];i;i=nxt[i]){
        j=to[i];if(vis[j]) continue;
        del(j,x);
    }
    for(i=head[x];i;i=nxt[i]){
        j=to[i];if(vis[j]) continue;
        S=sz[j];MX=inf;rt=0;
        getrt(j,x);
        divide(rt);
    }
}

int main(){
    register int i,j,ix,iy,iz;
    n=rd();k=rd();
    for(i=0;i<=k;++i) ct[i]=inf;
    for(i=1;i<n;++i){ix=rd()+1;iy=rd()+1;iz=rd();lk(ix,iy,iz);lk(iy,ix,iz);}
    MX=inf;S=n;getrt(1,0);divide(rt);
    if(ans<inf) printf("%d\n",ans);else printf("-1\n");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值