题意:给出一个包含n 个整数的数组,你需要回答若干询问。每次询问两个整数k和v,输出从左到右第k个V的下标(数组下标从左到右编号为1~n)。
[输入格式]
输入包含多组数据。每组数据第一行为两个整数n 和m (1<=n,m<=100000),第二行包含n个不超过10^6的正整数,即待查询的数组。以下m行每行包含两个整数k和V (1<=k<=n,I<=v<=10^6)。输入结束标志为文件结束符(EOF)。输入文件不超过5MB。[输出格式)
对于每个查询,输出查询结果。如果不存在,输出0。[分析]
本题有很多做法,下面描述一种编程复杂度小,时间效率也满足题目要求的方法。从查询的角度讲,如果能把输入组织成一个可以“直接读结果”的数据结构,是最好不过了。比如,如果data[v][k]就是答案,那该有多好!
事实上,这样的数据结构是存在的。首先,因为v的范围很大,这里的data不应该是个数组,而是一个STL 的map。也就是说,data[v]是指在data 这个map 中键v所对应的“值”。由于我们还要以data[v][k]这样的方式访问,data[v]的“值”应当是一个数组,保存整数v从左到右依次出现的下标(因此第k 次出现的下标就是data[v][k])。 由于不同整数出现的次数可以相差很大,data[v]不应是一一个定长数组,否则会有大量的空间浪费。换句话说,data[V]应该是一个变长数组,如vector<int>
这样,我们就可以利用vector 和map 这两个现成的数据结构简洁地解决本题,代码如下。
#include<bits/stdc++.h>
using namespace std;
int n, m;
map<int, vector<int> > mp;
int main()
{
while (~scanf("%d%d", &n, &m))
{
mp.clear();
for (int i = 1; i <= n; i++)
{
int x; scanf("%d", &x);
if (!mp.count(x)) mp[x] = vector<int>();
mp[x].push_back(i);
}
while (m--)
{
int k, v; scanf("%d%d", &k, &v);
if (!mp.count(v) || mp[v].size() < k) printf("0\n");
else printf("%d\n", mp[v][k-1]);
}
}
return 0;
}
/*
8 4
1 3 2 2 4 3 2 1
1 3
2 4
3 2
4 2
*/
自己一开始想到的方法:
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
int n, m;
map<int, int> vis;
map<pair<int, int>, int> mp;
int main()
{
while (~scanf("%d%d", &n, &m))
{
vis.clear(); mp.clear();
for (int i = 1; i <= n; i++)
{
int v; scanf("%d", &v);
if (vis.count(v)) vis[v]++;
else vis[v] = 1;
mp.insert(make_pair(make_pair(vis[v], v), i));
}
while (m--)
{
int k, v; scanf("%d%d", &k, &v);
printf("%d\n", mp[make_pair(k, v)]);
}
}
return 0;
}
/*
8 4
1 3 2 2 4 3 2 1
1 3
2 4
3 2
4 2
*/