luogu P4149 [IOI2011]Race

背景:

物理竞赛强势看错 ∞ ∞ 道题。
这么水的题目,可是还是太粗心了(毒奶一口,没奖)。
要被班主任骂死了。
数理化信竞赛均爆炸(不过也不重要了)。

点分治套路:
[ 1 ] . [1]. [1].收集所有儿子的信息,然后一次性合并,并排除不合法的情况。
[ 2 ] . [2]. [2].顺次处理儿子的信息,处理一个合并一个。

题目传送门:

https://www.luogu.org/problemnew/show/P4149

题意:

一棵树,选出尽量少的边使全值和为 m m m

思路:

就是顺次处理儿子的信息,处理一个合并一个。最后删去不合法的。
用一个 d p dp dp来统计答案即可。
卡常(我的做法不够优秀)。

代码( 90 p t s 90pts 90pts,卡常):

#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF int(1e8)
#define R register
#define I inline
using namespace std;
    int n,m,len=0,ans;
    int last[1000010],size[1000010],msize[1000010],dis[1000010],sum[1000010],d[1000010],f[1000010];
    bool bz[1000010];
    struct node{int x,y,z,next;} a[1000010];
int SIZE,MIN,ROOT,tot;
I int read()
{
    int x=0,f=1;
    char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())
        if(ch=='-') f=-1;
    for(;ch>='0'&&ch<='9';x=(x<<3)+(x<<1)+(ch^48),ch=getchar());
    return x*f;
}
void ins(int x,int y,int z)
{
    a[++len]=(node){x,y,z,last[x]}; last[x]=len;
}
void find_root(int x,int fa)
{
    size[x]=1;
    msize[x]=0;
    for(R int i=last[x];i;i=a[i].next)
    {
        int y=a[i].y;
        if(y==fa||bz[y]) continue;
        find_root(y,x);
        size[x]+=size[y];
        msize[x]=max(msize[x],size[y]);
    }
    msize[x]=max(msize[x],SIZE-size[x]);
    if(MIN>msize[x]) MIN=msize[x],ROOT=x;
}
void get_dis(int x,int fa)
{
    if(dis[x]<=m) ans=min(ans,d[x]+f[m-dis[x]]);
    for(R int i=last[x];i;i=a[i].next)
    {
        int y=a[i].y;
        if(y==fa||bz[y]) continue;
        d[y]=d[x]+1;
        dis[y]=dis[x]+a[i].z;
        get_dis(y,x);
    }
}
void update(int x,int fa,bool op)
{
    if(dis[x]<=m)
    {
        if(op) f[dis[x]]=min(f[dis[x]],d[x]); else f[dis[x]]=INF;
    }
    for(R int i=last[x];i;i=a[i].next)
    {
        int y=a[i].y;
        if(bz[y]||y==fa) continue;
        update(y,x,op);
    }
}
void dfs(int x)
{
    bz[x]=true;
    f[0]=0;
    for(R int i=last[x];i;i=a[i].next)
    {
        int y=a[i].y;
        if(bz[y]) continue;
        d[y]=1;
        dis[y]=a[i].z;
        get_dis(y,0);
        update(y,0,1);
    }
    for(R int i=last[x];i;i=a[i].next)
    {
        int y=a[i].y;
        if(bz[y]) continue;
        update(y,0,0);
    }
    for(R int i=last[x];i;i=a[i].next)
    {
        int y=a[i].y;
        if(bz[y]) continue;
        SIZE=size[y],MIN=n,ROOT=0;
        find_root(y,0);
        dfs(ROOT);
    }
}
int main()
{
    int x,y,z;
    n=read(),m=read();
    for(R int i=1;i<n;i++)
    {
        x++,y++;
        x=read(),y=read(),z=read();
        ins(x,y,z),ins(y,x,z);
    }
    ans=n;
    for(R int i=0;i<=m;i++)
        f[i]=INF;
    SIZE=n,MIN=n,ROOT=0;
    find_root(1,0);
    dfs(ROOT);
    printf("%d",ans==n?-1:ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值