[BZOJ2809][Apio2012]dispatching(dfs序+主席树)

本文介绍了一种利用主席树优化解决特定问题的方法。通过对树的DFS序进行处理,并使用主席树来维护子树内权值之和小于等于给定值的节点数量,解决了在每个忍者的子树中选取节点的问题。文章详细展示了从问题分析到具体实现的全过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

我是超链接

题解:

显然对于每一个忍者来说,我们只能选择其子树里的节点,这些节点的薪水和肯定是越小越好呀,那么我们的直接想法就是维护所在子树内权值和<=m的忍者个数,似乎可以用dfs序+主席树维护?

想要把子树收束在一个顶点似乎是不好实现,我们可以转化一下:舍弃以前的顶点,只按照dfs序建树,这样你虽然会使叶子节点收束全局,但你查询的时候用in[i]-1和out[i]一样可以收束

注意这里按照权值建立线段树,但是必须每一个节点一个区间,不能出现一个节点有两个忍者,虽然你可以将ta们乘起来,但你不能处理只选一个忍者的情况

这就要求我们离散的时候相同的也得分出个大小,这样STL就不行了,我们要用原始的数组法
p[i]表示排名为i的在a中的下标,这样b就是我们的离散数组了

代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
#define LL long long 
using namespace std;
const int N=100005;
int tot,nxt[N*2],point[N],v[N*2],in[N],out[N],dfn[N],sz,root[N],nn,c[N],b[N],p[N];
struct hh{int l,r,w;LL sum;}t[N*20];LL ans,l[N],m;
int cmp(int x,int y){return c[x]<c[y];}
void addline(int x,int y)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
    ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void dfs(int x,int fa)
{
    in[x]=++nn; dfn[nn]=x;
    for (int i=point[x];i;i=nxt[i])
      if (v[i]!=fa) dfs(v[i],x);
    out[x]=nn;
}
void insert(int &now,int l,int r,int x,LL v)
{
    t[++sz]=t[now]; now=sz;
    t[now].w++; t[now].sum+=v;
    if (l==r) return;
    int mid=(l+r)>>1;
    if (x<=mid) insert(t[now].l,l,mid,x,v);
    else insert(t[now].r,mid+1,r,x,v);
}
int qurry(int x,int y,int l,int r,LL m)
{
    if (l==r)
    {
        if (t[y].sum-t[x].sum<=m) return t[y].w-t[x].w;
        else return 0;
    }
    int mid=(l+r)>>1;LL sum=t[t[y].l].sum-t[t[x].l].sum;
    if (sum<=m) return t[t[y].l].w-t[t[x].l].w+qurry(t[x].r,t[y].r,mid+1,r,m-sum);
    else return qurry(t[x].l,t[y].l,l,mid,m);
}
int main()
{
    int n,x,s=0,gen;
    scanf("%d%lld",&n,&m);
    for (int i=1;i<=n;i++)
    {
        p[i]=i;
        scanf("%d%d%lld",&x,&c[i],&l[i]);
        if (x==0) gen=i;
        else addline(i,x);
    }
    sort(p+1,p+n+1,cmp);//p[i]排名为i的在a中的下标 
    for (int i=1;i<=n;i++) b[p[i]]=i;//离散数组 
    dfs(gen,0);
    for (int i=1;i<=n;i++)
    {
        root[i]=root[i-1];
        insert(root[i],1,n,b[dfn[i]],c[dfn[i]]);
    }
    for (int i=1;i<=n;i++)
    {
        LL aa=qurry(root[in[i]-1],root[out[i]],1,n,m);
        ans=max(ans,l[i]*aa);
    }
    printf("%lld",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值