bzoj2599 Race 点分治

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

树的点分治,用数组cnt[x]记录与根距离为x最小多少条边,然后每搜完一棵子树,再用这棵子树的信息来更新cnt,避免统计同一棵子树中的点对。

#include<iostream>
#include<cstdio>
#include<algorithm>
#define maxn 200005
using namespace std;
struct E{
    int to,nxt,d;
}b[maxn<<1];
int fst[maxn],tot=1;
void insert(int f,int t,int d)
{
    b[++tot]=(E){t,fst[f],d};fst[f]=tot;
    b[++tot]=(E){f,fst[t],d};fst[t]=tot;
}
int inf=1e9;
int n,K,root,sum,ans=1e9;
int mx[maxn],size[maxn];
bool vis[maxn];
void get_root(int x,int f)
{
    size[x]=1;mx[x]=0;
    for(int i=fst[x];i;i=b[i].nxt)
    {
        int v=b[i].to;
        if(!vis[v]&&f!=v)
        {
            get_root(v,x);
            size[x]+=size[v];
            mx[x]=max(mx[x],size[v]);
        }
    }
    mx[x]=max(mx[x],sum-size[x]);
    if(mx[x]<mx[root]) root=x;
}
int deep[maxn],dis[maxn];
int cnt[1000005],top,pre;
pair<int,int> a[maxn];
void get_dis(int x,int f)
{
    int p=dis[x];
    if(p>K) return ;
    a[++top]=make_pair(dis[x],deep[x]);
    ans=min(ans,deep[x]+cnt[K-p]);
    for(int i=fst[x];i;i=b[i].nxt)
    {
        int v=b[i].to;
        if(!vis[v]&&v!=f)
        {
            dis[v]=dis[x]+b[i].d;
            deep[v]=deep[x]+1;
            if(dis[v]<=K)
                get_dis(v,x);
        }
    }
}
void calc(int x)
{
    dis[x]=0;deep[x]=0;
    top=0;pre=1;
    for(int i=fst[x];i;i=b[i].nxt)
    {
        int v=b[i].to;
        if(!vis[v])
        {
            deep[v]=1;dis[v]=b[i].d;
            get_dis(v,x);
            for(int i=pre;i<=top;i++)
            {
                int p=a[i].first;
                if(p<=K)cnt[p]=min(cnt[p],a[i].second);
            }
            pre=top+1;
        }
    }
    for(int i=1;i<=top;i++)
    {
        int p=a[i].first;
        if(p<=K) cnt[p]=inf;
    }
    cnt[0]=0;
}
void solve(int x)
{
    calc(x);vis[x]=1;
    for(int i=fst[x];i;i=b[i].nxt)
    {
        int v=b[i].to;
        if(!vis[v])
        {
            sum=size[v];
            root=0;
            get_root(v,0);
            solve(root);
        }
    }
}
int main()
{
    scanf("%d%d",&n,&K);
    int u,v,d;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&u,&v,&d);
        u++;v++;
        insert(u,v,d);
    }
    mx[0]=maxn;sum=n;
    for(int i=1;i<=K;i++)cnt[i]=inf;
    get_root(1,0);
    solve(root);
    if(ans==inf) ans=-1;
    printf("%d",ans);
    return 0;
}

其他类似点分治题目3365,2152,1468

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值