HDU-5242 Game (贪心&&树链剖分&&线段树)

题目:

http://acm.hdu.edu.cn/showproblem.php?pid=5242

题意:

给出一颗以1节点为根的树,每个节点有各自的价值,有m次从根节点出发向下走到叶子节点的机会,每次会得到所有经过节点的权值,每个节点只有在第一次经过时有价值,求m次之后能够获得的最大权值。

思路:

典型的树链剖分题,只要找到所有重链的权值然后贪心找前m个的和就行了。

具体解法就是先dfs找到所有叶子节点从根走下来得到的总权值,排序之后将有重复路径的节点权值减去,这就是一个找重链的过程,对于每个节点,他的所有儿子中能够找到最大权值链的那一条就是和这个节点在同一条重链上的,其他儿子节点作为其他重链的新起点,最后结构造出了一个包含重链的树。每个节点都一定且仅在一条重链中,并且每条重链都包含一个叶子节点,所以只要找到权值最大的m条重链就是最大总权值了。


一开始并没有想到写树链剖分,用线段树写的。

前面的处理一样,找到每个叶子节点的总权值,然后以叶子节点建树。在dfs找叶子节点总权值时回溯处理出来每个节点在这个线段树中包含的区间。然后线段树求m次最大值就行了。每次query之后从该叶子节点向上搜索找到他的所有父节点对这些父节点在线段树中包含的叶子节点都进行区间更新,就是都减去这个节点的权值,保证已经更新过的节点就不更新了,所以整个更新的复杂度为O(n*logn)。找最大的m个节点的复杂度是O(m*logn)。


所以树链剖分的复杂度是O(2*n),线段树叶子节点是O(n*logn+m*logn),实际表现差不多。

代码:

树链剖分:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define MOD 1000000007
#define EPS 1e-6
#define N 112345
using namespace std;
struct node
{
    long long val,id;
    friend bool operator < (node a, node b)
    {
        return a.val > b.val;
    }
}p[N];
long long n,m,res,flag,tot;
vector<long long>zi[N];
long long fa[N],val[N],ans[N];
bool vis[N];
void init()
{
    for(int i=0;i<=n;i++)zi[i].clear();
    memset(vis,0,sizeof(vis));
    res=0;tot=0;
}
void dfs(long long now, long long vall)
{
    int num=zi[now].size();
    if(num==0)
        p[tot].id=now, p[tot++].val=val[now]+vall;
    else
        for(int i=0;i<num;i++)
            dfs(zi[now][i],vall+val[now]);
}
long long dfs1(long long now)
{
    if(vis[now])return 0;
    vis[now]=1;
    return val[now]+dfs1(fa[now]);
}
int main()
{
    long long i,j,k,kk,cas,T,t,x,y,z;
    scanf("%I64d",&T);
    cas=0;
    while(T--)
    {
        scanf("%I64d%I64d",&n,&m);
        init();
        for(i=1;i<=n;i++)scanf("%I64d",&val[i]);
        for(i=1;i<n;i++)
        {
            scanf("%I64d%I64d",&x,&y);
            zi[x].push_back(y);
            fa[y]=x;
        }
        dfs(1,0);
        sort(p,p+tot);
        for(i=0;i<tot;i++)
            ans[i]=dfs1(p[i].id);
        sort(ans,ans+tot);
        for(i=tot-1,j=0;i>=0&&j<m;j++,i--)res+=ans[i];
        printf("Case #%I64d: %I64d\n",++cas,res);
    }
    return 0;
}

线段树:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define MOD 1000000007
#define EPS 1e-6
#define N 112345
using namespace std;
struct node
{
    long long sum,side;
}sum[N<<2],ttt;
long long n,res,flag,tot;
long long a[N],b[N],hehe[N],xixi[N];
#define root 1 , tot , 1
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
vector<long long>zi[N];
long long fa[N];
bool vis[N];
long long val[N];
long long add[N<<2];
void pushUp(long long rt)
{
    if(sum[rt<<1].sum>=sum[rt<<1|1].sum)
    {
        sum[rt]=sum[rt<<1];
    }
    else
    {
        sum[rt]=sum[rt<<1|1];
    }
}
void pushDown(long long l,long long r,long long rt)
{
    if(add[rt])
    {
        long long m = (l+r)>>1;
        add[rt<<1] += add[rt];
        add[rt<<1|1] += add[rt];
        sum[rt<<1].sum += add[rt];
        sum[rt<<1|1].sum += add[rt];
        add[rt] = 0;
    }
}
void update(long long l,long long r,long long rt,long long ql,long long qr,long long val)
{
    if(l>qr||ql>r)return;
    if(l>=ql&&r<=qr)
    {
        sum[rt].sum += val;
        add[rt] += val;
        return;
    }
    pushDown(l,r,rt);
    long long m = (l+r)>>1;
    if(ql<=m)update(lson,ql,qr,val);
    if(qr>m)update(rson,ql,qr,val);
    pushUp(rt);
}
void build(long long l,long long r,long long rt)
{
    add[rt]=0;
    if(l == r)
    {
        sum[rt].sum=hehe[res++];
        sum[rt].side=xixi[res-1];
        return;
    }
    long long m = (l+r)>>1;
    build(lson);
    build(rson);
    pushUp(rt);
}
node query(long long l,long long r,long long rt,long long ql,long long qr)
{
    if(l>qr||ql>r)
        return ttt;
    if(l>=ql&&r<=qr)
        return sum[rt];
    pushDown(l,r,rt);
    long long m = l+r>>1;
    node x=query(l,m,rt<<1,ql,qr);
    node y=query(m+1,r,rt<<1|1,ql,qr);
    return x.sum>=y.sum?x:y;
}
void init()
{
    for(int i=0;i<=n;i++)zi[i].clear();
    memset(vis,0,sizeof(vis));
    ttt.sum=-1;
}
void dfs(int now,long long vall)
{
    long long len=zi[now].size();
    if(len==0)
    {
        hehe[tot++]=val[now]+vall;
        xixi[tot-1]=now;
        a[now]=b[now]=tot-1;
        return ;
    }
    for(int i=0;i<len;i++)
        dfs(zi[now][i],vall+val[now]);
    long long x=INF,y=-1;
    for(int i=0;i<len;i++)
    {
        x=min(x,a[zi[now][i]]);
        y=max(y,b[zi[now][i]]);
    }
    a[now]=x;b[now]=y;
}
void dfs1(long long now)
{
    if(vis[now])return;
    vis[now]=true;
    update(root,a[now]+1,b[now]+1,-val[now]);
    if(now==1)return;
    dfs1(fa[now]);
}
int main()
{
    long long i,j,k,kk,cas,T,t,x,y,z;
    scanf("%I64d",&T);
    cas=0;
    while(T--)
    {
        long long m;
        scanf("%I64d%I64d",&n,&m);
        init();
        for(i=1;i<=n;i++)scanf("%I64d",&val[i]);
        for(i=1;i<n;i++)
        {
            scanf("%I64d%I64d",&x,&y);
            zi[x].push_back(y);
            fa[y]=x;
        }
        tot=0;
        dfs(1,0);
        res=0;
        build(root);
        long long res=0;
        for(i=0;i<m;i++)
        {
            node t=query(root,1,tot);
            res+=t.sum;
            dfs1(t.side);
        }
        printf("Case #%I64d: %I64d\n",++cas,res);
    }
    return 0;
}












  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值