UVA ~ 11991 ~ Easy Problem from Rujia Liu? (map + vector)

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值