给定一个长度为n的序列a[1]到a[n]
q次询问:给定i,j,k,求出a[i]到a[j]中大于k的个数
主席树裸题,但是懒得写
刚开始写了个莫队+树状数组,复杂度是没问题的\(O(n\sqrt{q}logn)\),但是wa了,也懒得调了
然后还有种离线做法
将询问按\(k\)降序排序,对\(a\)降序排序
我们用一个指针\(l\),如果\(l\)没到头并且\(a[l] > k\),那么就往后移指针,并且把对应位置单点加\(1\),答案就转化为了区间查询和
因为\(l\)从头到尾最多移一次,所以时间复杂度\(O(nlogn)\)
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
const int N = 3e4;
const int M = 2e5;
#define zrt k << 1
#define yrt k << 1 | 1
using namespace std;
struct ad
{
int id,v;
}a[N + 5];
struct node
{
int l,r,k,id;
}q[M + 5];
int n,m,ans[M + 5],cnt;
int cmp1(ad x,ad y)
{
return x.v > y.v;
}
int cmp2(node x,node y)
{
return x.k > y.k;
}
struct dd
{
int su,tag;
};
struct Seg
{
dd s[N * 4 + 5];
void pushup(int k)
{
s[k].su = s[zrt].su + s[yrt].su;
}
void add(int k,int l,int r,int x,int z)
{
if (l == r)
{
s[k].su += z;
return;
}
int mid = l + r >> 1;
if (x <= mid)
add(zrt,l,mid,x,z);
else
add(yrt,mid + 1,r,x,z);
pushup(k);
}
int query(int k,int l,int r,int x,int y)
{
if (l >= x && r <= y)
return s[k].su;
int mid = l + r >> 1;
if (x > mid)
return query(yrt,mid + 1,r,x,y);
else
if (y <= mid)
return query(zrt,l,mid,x,y);
else
return query(zrt,l,mid,x,y) + query(yrt,mid + 1,r,x,y);
}
}tree;
int main()
{
scanf("%d",&n);
for (int i = 1;i <= n;i++)
{
scanf("%d",&a[i].v);
a[i].id = i;
}
scanf("%d",&m);
for (int i = 1;i <= m;i++)
{
scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].k);
q[i].id = i;
}
sort(a + 1,a + n + 1,cmp1);
sort(q + 1,q + m + 1,cmp2);
int l = 1;
for (int i = 1;i <= m;i++)
{
while (l <= n && a[l].v > q[i].k)
{
tree.add(1,1,n,a[l].id,1);
l++;
}
ans[q[i].id] = tree.query(1,1,n,q[i].l,q[i].r);
}
for (int i = 1;i <= m;i++)
printf("%d\n",ans[i]);
return 0;
}