P1533 可怜的狗狗

Link

题目背景

小卡由于公务需要出差,将新家中的狗狗们托付给朋友嘉嘉,但是嘉嘉是一个很懒的人,他才没那么多时间帮小卡喂狗狗。

题目描述

小卡家有 N 只狗,由于品种、年龄不同,每一只狗都有一个不同的漂亮值。漂亮值与漂亮的程度成反比(漂亮值越低越漂亮),吃饭时,狗狗们会按顺序站成一排等着主人给食物。

可是嘉嘉真的很懒,他才不肯喂这么多狗呢,这多浪费时间啊,于是他每次就只给第i只到第j只狗中第k漂亮的狗狗喂食(好狠心的人啊)。而且为了保证某一只狗狗不会被喂太多次,他喂的每个区间(i,j)不互相包含。

输入格式

第一行输入两个数n,m,你可以假设n<300001 并且 m<50001;m表示他喂了m 次。

第二行n个整数,表示第i只狗的漂亮值为ai。

接下来m行,每行3个整数i,j,k表示这次喂食喂第i到第j只狗中第k漂亮的狗的漂亮值。

输出格式

M行,每行一个整数,表示每一次喂的那只狗漂亮值为多少。

输入输出样例

输入 #1

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

输出 #1

3
2

题解

一句话题意:区间第 \(k\) 大数

这道题的做法有很多种,比如说平衡树加双指针或者裸的主席树。

由于蒟蒻我不会写平衡树(那只能用主席树水过去了)。

还是口胡一下平衡树的做法吧。

因为他的询问区间不重叠,所以我们可以给询问区间按左端点拍一下序。

搞两个指针像莫队一样来回反复横跳,每次在平衡树中删除或加入一个数。

再用平衡树求解区间第 \(k\) 大数。

至于线段树的做法,就是裸的主席树啦(以后会把主席树的坑补上的)。

主席树Code

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 3e5+10;
int n,m,l,r,tot,k,cnt;
int root[N*22],a[N],b[N];
inline int read()
{
    int s = 0,w = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9'){s =s * 10+ch - '0'; ch = getchar();}
    return s * w;
} 
struct node
{
    int lc,rc,sum;
}tr[N*22];
int build(int l,int r)//建空树
{
    int p = ++tot;
    if(l == r)
    {
        tr[p].sum = 0;
        return p;
    }
    int mid = (l+r)>>1;
    tr[p].lc = build(l,mid);
    tr[p].rc = build(mid+1,r);
    return p;
}
int insert(int now,int L,int R,int x,int val)//新开一个版本
{
    int p = ++tot;
    tr[p].lc = tr[now].lc;//先继承上一个版本的状态
    tr[p].rc = tr[now].rc;
    tr[p].sum = tr[now].sum;
    if(L == R)
    {
        tr[p].sum += val;
        return p;
    }
    int mid = (L + R)>>1;
    if(x <= mid)//递归改左儿子
    {
        tr[p].lc = insert(tr[now].lc,L,mid,x,val);
    }
    if(x > mid)//递归右儿子
    {
        tr[p].rc = insert(tr[now].rc,mid+1,R,x,val);
    }
    tr[p].sum = tr[tr[p].lc].sum + tr[tr[p].rc].sum;//up 一下
    return p;
}
int query(int p,int q,int L,int R,int k)//区间第 k 大的数
{
    if(L == R){return L;}//如果 L== R 就说明我们已经找到了答案
    int mid = (L + R)>>1;
    int tmp = tr[tr[q].lc].sum - tr[tr[p].lc].sum;
    if(k <= tmp)// k 和 tmp 一定不要写反了 我在这里卡了好几回
    {
        return query(tr[p].lc,tr[q].lc,L,mid,k);
    } 
    if(k > tmp)
	{
		return query(tr[p].rc,tr[q].rc,mid+1,R,k-tmp);
	}
}
int main()
{
    n = read(); m = read();
    for(int i = 1; i <= n; i++)
    {
        a[i] = read();
        b[++cnt] = a[i];
    }
    sort(b+1,b+cnt+1);//先离散化一下
    int t = unique(b+1,b+cnt+1)-b-1;
    for(int i = 1; i <= n; i++)
    {
        a[i] = lower_bound(b+1,b+t+1,a[i])-b;
    }
    root[0] = build(1,n); //建立一个空树
    for(int i = 1; i <= n; i++)
    {
        root[i] = insert(root[i-1],1,n,a[i],1);//依次把每个版本插进去
    }
    for(int i = 1; i <= m; i++)
    {
        l = read(); r = read(); k = read();
        printf("%d\n",b[query(root[l-1],root[r],1,t,k)]);//区间第k大的数
    }
    return 0;
}

平衡树代码:

咕咕咕

posted @ 2020-09-05 20:53  genshy  阅读(175)  评论(0编辑  收藏  举报