牛客网【每日一题】4月30日题目精讲 换个角度思考
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
输入描述:
第一行两个整数n,m 第二行n个整数表示序列a的元素,序列下标从1开始标号,保证1 ≤ ai ≤ 105
之后有m行,每行三个整数(l,r,k),保证1 ≤ l ≤ r ≤ n,且1 ≤ k ≤ 105
输出描述:
对于每一个询问,输出一个整数表示答案后回车
示例1
输入
5 1
1 2 3 4 5
1 5 3
输出
3
备注:
数据范围
1 ≤ n ≤ 105
1 ≤ m ≤ 105
题解:
主席树,树状数组等都可以做
主席树做法:
简单提一句主席树
主席树的本体其实是线段树,也就是很多棵线段树,用以存一段数字区间出现次数,主席树经常用于求一个序列内的第 k 小
在这里,主席树就是提前预处理好每个点的权值线段树,查询时,可以直接用r时刻前缀小于x的数量减去l-1的数量,剩下的就是[l,r]区间值
也就是[L,R]=[1,R]-[1,L-1],查询后两者相减即可
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+2;
int n,m;
int a[maxn];
vector<int>edge[maxn*4];
void build(int id,int l,int r)
{
edge[id].clear();
for(int i=l;i<=r;i++) edge[id].push_back(a[i]);
sort(edge[id].begin(),edge[id].end());
if(l==r) return;
int mid=(l+r)>>1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
}//建树
int query(int id,int L,int R,int l,int r,int k)
{
int sum=0;
vector<int>::iterator it;
if(l<=L&&R<=r)
{
it=upper_bound(edge[id].begin(),edge[id].end(),k);//二分查找,第一个大于k的数,返回地址
return it-edge[id].begin();//两者做差的个数
}
int mid=(L+R)/2;
if(l<=mid)
{
sum+=query(id*2,L,mid,l,r,k);
}
if(mid<r)
{
sum+=query(id*2,mid+1,R,l,r,k);
}
return sum;
}
int main()
{
int l,r,k;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) cin>>a[i];
build(1,1,n);
while(m--)
{
cin>>l>>r>>k;
printf("%d\n",query(1,1,n,l,r,k));
}
return 0;
}
代码:
树状数组:
首先需要离线操作,我们每次查询都是要知道左右区间以及x,相当于同时有两个东西未知需要操作。通过离散后,我们可以控制其中一部分,另外一部分未知变化,例如:第一种我们查询[l,r]内有多少数满足条件,我们就要保证所有数都小于x;第二种如何问有多少数小于等于x,就保证范围取定于[l,r]。这样更高效,当然占空间更多。
简单的说:
离线操作:读入所有的操作数据,然后一次性处理。
在线操作:每读入一个操作数据,就进行一次操作。
第一种:
我们在保证所有数都是小于等于x的情况下来查询[l,r]有多少个数
每次询问[l,r,x]之前,把小于等于x的ai都加入加入到一个另外的位置上,这样里面存放的都是满足条件的数,直接询问即可
第二种:
保证所有数都在[l,r]区间的情况下,查询小于等于x的数量
我们可以在离线时,将[l,r]区间问题转化成 [ 1 , l-1 ] , [ 1 , r ] 两个区间,这样就可以用树状数组来解决
(这是邓老师的讲解,我加入自己的理解写出来的)
代码写完再更
更扯淡的方法!!!
这题貌似优化暴力就能过。。。
就是直接模拟,不知为啥过了。。快读都还没用上
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 3;
int a[N];
int main() {
int n,m;
cin>>n>>m;
for (int i = 1; i <= n; ++i) cin>>a[i];
while (m--) {
int l,r,k;
cin>>l>>r>>k;
int sum = 0;
for (int i = l; i <= r; ++i)
if (a[i] <= k) ++sum;
printf("%d\n", sum);
}
return 0;
}