P1533 可怜的狗狗
题目背景
小卡由于公务需要出差,将新家中的狗狗们托付给朋友嘉嘉,但是嘉嘉是一个很懒的人,他才没那么多时间帮小卡喂狗狗。
题目描述
小卡家有 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;
}
平衡树代码:
咕咕咕