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

题目:给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 1000000

思路:先找到根,然后求出经过根的路径数最小的方案。在处理一个节点时,用已处理的信息加上当前点的信息来更新答案,t[i]表示距离根为i的节点的最小深度,dis[i]表示i节点离根的距离,ans=min(ans,t[k-dis[i]]+d[i])。处理完根的一个子树后再更新一下信息,可以保证答案经过根。对每个子树分治一下就好了。

#include <cstdio>
#include <vector>
using namespace std;

inline char tc(void)
{
    static char fl[1000000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,1000000,stdin),A==B)?EOF:*A++;
}

inline int read(void)
{
    int a=0;static char c;
    while((c=tc())<'0'||c>'9');
    while(c>='0'&&c<='9')
        a=a*10+c-'0',c=tc();
    return a;
}

struct Edge
{
    int to,cost;
    Edge(int t,int c):to(t),cost(c){}
};
int n,k,t[1000001],mx[200001],size[200001],d[200001],dis[200001],sum,root,ans=1e9;
char vis[200001];
vector<Edge>edge[200001];

void dfs1(int x,int fa)
{
    register int i;
    size[x]=1,mx[x]=0;
    for (i=0;i<edge[x].size();++i)
        if(!vis[edge[x][i].to]&&edge[x][i].to!=fa)
            dfs1(edge[x][i].to,x),mx[x]=max(mx[x],size[edge[x][i].to]),size[x]+=size[edge[x][i].to];
    mx[x]=max(mx[x],sum-size[x]);mx[x]<mx[root]?root=x:0;
    return ;
}

void add(int x,int fa,int flag) 
{
    register int i;
    flag?t[dis[x]]=min(t[dis[x]],d[x]):t[dis[x]]=1e9;
    for (i=0;i<edge[x].size();++i)
        if(!vis[edge[x][i].to]&&edge[x][i].to!=fa&&dis[edge[x][i].to]<=k)
            add(edge[x][i].to,x,flag);
    return ;
}

void cal(int x,int fa)
{
    register int i;
    ans=min(ans,t[k-dis[x]]+d[x]);
    for (i=0;i<edge[x].size();++i)
        if(!vis[edge[x][i].to]&&edge[x][i].to!=fa)
        {
            d[edge[x][i].to]=d[x]+1;
            dis[edge[x][i].to]=dis[x]+edge[x][i].cost;
            if(dis[edge[x][i].to]<=k)
                cal(edge[x][i].to,x);
        }
    return ;
}

void work(int x)
{
    register int i;
    vis[x]=1,t[0]=0;
    for (i=0;i<edge[x].size();++i)
        if(!vis[edge[x][i].to])
            dis[edge[x][i].to]=edge[x][i].cost,d[edge[x][i].to]=1,
            dis[edge[x][i].to]<=k?cal(edge[x][i].to,0),add(edge[x][i].to,0,1),0:0;
    for (i=0;i<edge[x].size();++i)
        if(!vis[edge[x][i].to])
            dis[edge[x][i].to]<=k?add(edge[x][i].to,0,0),0:0;
    for (int i=0;i<edge[x].size();++i)
        if(!vis[edge[x][i].to])
            sum=size[edge[x][i].to],root=0,dfs1(edge[x][i].to,0),work(root);
    return ;
}

int main(void)
{
    register int i,x,y,z;
    n=read(),k=read();
    for (i=1;i<=k;++i)
        t[i]=1e9;
    for (i=1;i<n;++i)
        x=read()+1,y=read()+1,z=read(),edge[x].push_back(Edge(y,z)),edge[y].push_back(Edge(x,z));
    sum=n,mx[0]=1e9,dfs1(1,0),work(root);
    return ans==1e9?puts("-1"):printf("%d",ans),0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值