【贪心+ST算法+堆】BZOJ4458[GTY的OJ]题解

39 篇文章 2 订阅
11 篇文章 0 订阅

题目概述

n 个目录,每个目录有一个父亲目录和靠谱值,一个题目由若干个目录构成,其中任何一个目录都是别的目录的子孙和祖先,一个题目的靠谱值是所有目录靠谱值的和。需要选 k 个目录数量范围在 [L,R] 且不相同的题目,使得靠谱值最大。

解题报告

BZOJ2006套路是一样的,只不过做到了树上。

所以我们使用树上RMQ就行了。

示例程序

#include<cstdio>
#include<cmath>
#include<queue>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=500000,Log=19;

int n,K,L,R,sum[maxn+5];
int dep[maxn+5],fa[maxn+5][Log+5],RMQ[maxn+5][Log+5];
LL ans;

inline bool Eoln(char ch) {return ch==10||ch==13||ch==EOF;}
inline char readc()
{
    static char buf[100000],*l=buf,*r=buf;
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    if (l==r) return EOF; else return *l++;
}
inline int readi(int &x)
{
    int tot=0,f=1;char ch=readc(),lst='+';
    while ('9'<ch||ch<'0') {if (ch==EOF) return EOF;lst=ch;ch=readc();}
    if (lst=='-') f=-f;
    while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=readc();
    return x=tot*f,Eoln(ch);
}
int Miner(int i,int j) {if (sum[fa[i][0]]<sum[fa[j][0]]) return i; else return j;}
void make_ST()
{
    for (int j=1;j<=Log;j++)
    for (int i=1;i<=n;i++)
        fa[i][j]=fa[fa[i][j-1]][j-1];
    for (int j=1;j<=Log;j++)
    for (int i=1;i<=n;i++) if (dep[i]>=(1<<j))
        RMQ[i][j]=Miner(RMQ[i][j-1],RMQ[fa[i][j-1]][j-1]);
}
struct data
{
    int i,L,R,t;
    data(int a,int b,int c,int d) {i=a;L=b;R=c;t=d;}
    bool operator < (const data &c) const
        {return sum[i]-sum[fa[t][0]]<sum[c.i]-sum[fa[c.t][0]];}
};
priority_queue<data> Heap;
int Askfa(int x,int len)
{
    for (int j=Log;j>=0;j--)
        if (len&(1<<j)) x=fa[x][j],len-=1<<j;
    return x;
}
int Askmax(int x,int y)
{
    int MIN=RMQ[x][0];
    for (int j=Log;j>=0;j--)
        if (dep[fa[y][j]]>=dep[x]) MIN=Miner(MIN,RMQ[y][j]),y=fa[y][j];
    return MIN;
}
int main()
{
    freopen("program.in","r",stdin);
    freopen("program.out","w",stdout);
    readi(n);
    for (int i=1;i<=n;i++) readi(fa[i][0]);
    for (int i=1,x;i<=n;i++)
    {
        readi(x);sum[i]=sum[fa[i][0]]+x;
        dep[i]=dep[fa[i][0]]+1;RMQ[i][0]=i;
    }
    make_ST();readi(K);readi(L);readi(R);
    for (int i=1;i<=n;i++) if (dep[i]>=L)
    {
        int l=max(1,Askfa(i,R-1)),r=Askfa(i,L-1);
        Heap.push(data(i,l,r,Askmax(l,r)));
    }
    while (K--)
    {
        data now=Heap.top();Heap.pop();ans+=sum[now.i]-sum[fa[now.t][0]];
        if (now.t!=now.L)
        {
            int ft=fa[now.t][0];
            Heap.push(data(now.i,now.L,ft,Askmax(now.L,ft)));
        }
        if (now.t!=now.R)
        {
            int len=dep[now.i]-dep[now.t],st=Askfa(now.i,len-1);
            Heap.push(data(now.i,st,now.R,Askmax(st,now.R)));
        }
    }
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值