题目描述:
时间限制:C/C++ 3秒,其他语言6秒
空间限制:C/C++ 256M,其他语言512M
为了不断优化推荐效果,今日头条每天要存储和处理海量数据。假设有这样一种场景:我们对用户按照它们的注册时间先后来标号,对于一类文章,每个用户都有不同的喜好值,我们会想知道某一段时间内注册的用户(标号相连的一批用户)中,有多少用户对这类文章喜好值为k。因为一些特殊的原因,不会出现一个查询的用户区间完全覆盖另一个查询的用户区间(不存在L1<=L2<=R2<=R1)。
思路:
我的思路:
因为询问区间保证了不会出现覆盖,那么如果将询问区间对左端点排序的话,就会出现下图这样
也就是说,如果对左端点排序,右端点一定是不断变大的。因为l1<=l2,所以r2>r1一定成立,否则就会与条件冲突。
所以我们维护双指针,用sum[maxn]标记离散化后的数i在已经扫过的区域内出现的次数的和。
右指针右移,sum[a[i]]++,左指针右移,sum[a[i]]--,这样就像是一个前缀和的性质,扫一遍复杂度O(n),还有离散化操作,应该是O(nlogn)的复杂度。
但是我只过了70%,没有检查出错,可能是数据随机并没有满足题目中的条件,也希望各位大佬帮我查查错。
#include <bits/stdc++.h>
using namespace std;
const int maxn=3e5+10;
vector<int>v;
int getid(int cur){
return lower_bound(v.begin(),v.end(),cur)-v.begin()+1;
}
struct Node{
int l,r;
int k;
int id;
}node[maxn];
int a[maxn];
int sum[maxn];
int ans[maxn];
int cmp(const Node a,const Node b){
return a.l<b.l||(a.l==b.l&&a.r<b.r);
}
signed main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
v.push_back(a[i]);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
int q;
scanf("%d",&q);
for(int i=1;i<=q;i++){
scanf("%d%d%d",&node[i].l,&node[i].r,&node[i].k);
node[i].id=i;
}
sort(node+1,node+q+1,cmp);
int l=1,r=1;
int cur=1;
while(cur<=q){
while(r<=node[cur].r){
sum[getid(a[r])]++;
r++;
}
while(l<node[cur].l){
sum[getid(a[l])]--;
l++;
}
int now=sum[getid(node[cur].k)];
ans[node[cur].id]=now;
cur++;
}
for(int i=1;i<=q;i++){
printf("%d\n",ans[i]);
}
return 0;
}
大佬的思路:
简单明了,维护一个值k的出现序列vector,如果有询问l,r,k,直接在vector中二分到l-1和r这个区间,看这个区间的长度有多长,就说明这个区间内有多少个k,不得不说这是我见过最简单明了的思路了。
#include <bits/stdc++.h>
using namespace std;
map<int, vector<int> >ma;
int n, q, t;
int l, r, k;
int solve(int l, int r, int k) {
if (ma[k].size() == 0) return 0;
auto it1 = lower_bound(ma[k].begin(), ma[k].end(), l);
auto it2 = upper_bound(ma[k].begin(), ma[k].end(), r);
return it2 - it1;
}
int main() {
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> t;
ma[t].push_back(i);
}
cin >> q;
for (int i = 0; i < q; ++i) {
cin >> l >> r >> k;
cout << solve(l, r, k) << endl;
}
return 0;
}