题意:给出一个包含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 */