题意:有最多N个的一组数组A,给定Q组问题,每组问题又(xi,ki),找出数字xi出现第ki次的下标,若没有则输出-1。
数据范围:
1<=N<=2e5;
1<=Q<=2e5;
0<=ai<=1e9;
难点:数据量大,卡时间;以数字为索引存位置,数字过大导致数组范围不够;普通遍历查找时间复杂度过大。
正确题解:
- 使用map进行存储数字和出现次数,定义一个结构体,存储值、次数和当前位置,然后对结构体根据值排序,使用二分查找找到当前次数在哪个位置,输出位置;
- 看题解,说是“逻辑有双层映射,嵌套哈希表”
第一种普通解法:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e6+10;
struct node{
int num;
int ti;//出现的次数,方便确认输出的地址
int dis;
}a[N];//下标无含义
map<int, int> mp;
//存数值和出现次数
//解决数值过大无法与出现次数在数组中对应的情况
bool cmp(node a,node b)
{
if(a.num==b.num) return a.ti<b.ti;
else return a.num<b.num;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i].num);
mp[a[i].num]++;
a[i].ti=mp[a[i].num];
a[i].dis=i;
}
sort(a+1,a+1+n,cmp);
while(m--)
{
int k;
int nu;
scanf("%d%d",&k,&nu);
if(mp[k]<nu) cout<<-1<<endl;
else
{
int l,r,mid;
l=1,r=n;
while(l<r)//找到k第一次出现的位置,l==r==第一次出现位置
{
mid=(l+r)/2;
if(a[mid].num<k)
{
l=mid+1;
}
else r=mid;
}
//cout<<l<<" "<<r<<" "<<mid;
cout<<a[l+nu-1].dis<<endl;
//cout<<l" "<<nu" "<<" ";
//第nu次出现的位置等于l+nu-a[l].ti
//第一次的出现位置+nu-1==第nu次的出现位置
}
}
return 0;
}
第二种 – 哈希表
这个速度要快好多!!
#include<bits/stdc++.h>
using namespace std;
typedef pair<int ,int > pii;
int main()
{
int n,q;
scanf("%d%d",&n,&q);
map<int , map<int, int> > mp;
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
if(!mp.count(x))
{
mp[x][1]=i;
}
else
{
int t=mp[x].size();
mp[x][t+1]=i;
}
}
while(q--)
{
int x,k;
scanf("%d%d",&x,&k);
if(mp[x][k])
{
printf("%d\n",mp[x][k]);
}
else printf("-1\n");
}
return 0;
}