野生动物园

这篇博客介绍了如何利用线段树解决区间第K大问题。通过构建N个线段树,每个树表示从1加到i时a[i]在各个区间[l,r]内的相对大小,以此实现快速查询。为了处理大范围问题,文章提出了使用可持久化数据结构,避免重复构建完整的线段树,提高了效率。" 119883604,5637686,Selenium:掌握switch_to.active_element获取焦点元素,"['测试工具', 'selenium', 'javascript']

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

Problem A:野生动物园

Time Limit:30000MS  Memory Limit:65536K
Total Submit:33 Accepted:16

Description

有一个很大的野生动物园。这个动物园坐落在一个狭长的山谷内,这个区域从南到北被划分成N个区域,每个区域都饲养着一头狮子。这些狮子从北到南编号为1,2,3,…,N。每头狮子都有一个觅食能力值Ai,Ai越小觅食能力越强。饲养员cmdButtons决定对狮子进行M次投喂,每次投喂都选择一个区间[I,J],从中选取觅食能力值第K强的狮子进行投喂。值得注意的是,cmdButtons不愿意对某些区域进行过多的投喂,他认为这样有悖公平。因此cmdButtons的投喂区间是互不包含的。你的任务就是算出每次投喂后,食物被哪头狮子吃掉了。 

Input

输入文件第一行有两个数N和M。此后一行有N个数,从南到北描述狮子的觅食能力值。此后M行,每行描述一次投喂。第t+2的三个数I,J,K表示在第t次投喂中,cmdButtons选择了区间[I,J]内觅食能力值第K强的狮子进行投喂。 

Output

有M行,每行一个整数。第i行的整数表示在第i次投喂中吃到食物的狮子的觅食能力值。 

Sample Input

7 2
1 5 2 6 3 7 4
1 5 3
2 7 1

Sample Output

3
2
数据范围:
对于50%的数据,有1<=N<=10000,1<=M<=500;
对于100%的数据,有1<=N<=100000,1<=M<=50000。

[Submit]   [Go Back]   [Status]   [Clarify]


线段树,

构建N个线段树,维护 其使第i个线段树表示加元素,从1加到i时,a[i]在每条线段[l,r]中为第几大,

这样可以快速求得区间第k大


由于范围较大,可以使用可持久化数据结构,不必完整的构建N个线段树,中间未更新的节点不必重新建造


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

struct node
{
    int l,r,len;
}t[5000010];

int n,m,N;
int a[100010],b[100010],root[100010];

int build (int l,int r)
{
    int i=++N;
    if (l!=r)
    {
        int mid=(l+r)>>1;
        t[i].l=build( l ,mid );
        t[i].r=build( mid+1,r);
    }
    return i;
}

int change(int i, int l, int r ,int k )
{
    ++N; t[N]=t[i]; i=N;
    t[i].len++;
    if (l!=r)
    {
        int mid=(l+r)>>1;
        if (k<=mid) t[i].l=change(t[i].l, l, mid, k);
        else
            t[i].r=change(t[i].r, mid+1, r, k);
    }
    return i;
}

int query(int i1,int i2,int l,int r ,int k)
{
    if (l==r) return l;
    else
    {
        int mid=(l+r) >>1;
        if ( k<= t[ t[i1].l].len - t[ t[i2].l].len ) return query( t[i1].l, t[i2].l, l, mid, k);
        else
            return query( t[i1].r, t[i2].r, mid+1, r, k-( t[ t[i1].l ].len- t[t[i2].l].len ) );
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1; i<=n; i++)
        scanf("%d",&a[i]);
    memcpy(b,a,sizeof(a));
    sort(b+1,b+1+n);
    N=1;
    for (int i=2; i<=n; i++)
        if (b[i]!=b[i-1] )  b[++N]=b[i];
    root[0]=build(1,n);
    for (int i=1; i<=n; i++)
        root[i]=change(root[i-1],1,n,upper_bound(b+1,b+1+n,a[i])-(b+1) );
    for (int i=1; i<=m; i++)
    {
        int x,y,k;
        scanf("%d%d%d",&x,&y,&k);
        printf("%d\n",b[ query( root[y],root[x-1] ,1, n, k ) ] );
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值