美团点评CodeM资格赛部分问题题解

优惠券

注意的点是任何时刻一张优惠券只能存在一个,如果同一张优惠券还没被使用又购入,就看上一次购入与该次购入之间有没有问号可以替代使用,使用优惠券同理。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <set>

using namespace std;

int len;

int main()
{
    string line;
    mywhile:
    while (cin >> len) {
        getchar();
        unordered_set<string> cards;
        set<int> unknowns;
        unordered_map<string, int> last_out, last_in;
        for (int i = 1; i <= len; ++i) {
            getline(cin, line);
            if (line[0] == 'I') {
                string x = line.substr(2);
                if (cards.count(x) == 0) {
                    cards.insert(x);
                } else {
                    int last_t = ( last_in.count(x) == 1 ? last_in[x] : 0 );
                    set<int>::iterator it = unknowns.lower_bound(last_t);
                    if (it == unknowns.end()) {
                        cout << i << endl;
                        goto mywhile;
                    } else {
                        unknowns.erase(it);
                    }
                }
                last_in[x] = i;
            } else if (line[0] == 'O') {
                string x = line.substr(2);
                if (cards.count(x) == 1) {
                    cards.erase(x);
                } else {
                    int last_t = ( last_out.count(x) == 1 ? last_out[x] : 0 );
                    set<int>::iterator it = unknowns.lower_bound(last_t);
                    if (it == unknowns.end()) {
                        cout << i << endl;
                        goto mywhile;
                    } else {
                        unknowns.erase(it);
                    }
                }
                last_out[x] = i;
            } else if (line == "?") {
                unknowns.insert(i);
            }
        }
        cout << -1 << endl;
    }
    return 0;
}

送外卖

https://www.nowcoder.com/question/next?pid=5513596&qid=104903&tid=8742003

挺好的题。我的做法是建图——将每个小区抽象为图节点,小区i有两条出边,出边a到达小区i+a[i],出边b到达小区i+b[i](如果到达的小区不存在就不用加边)。问题即转化为在一个有向图中,判断节点0n-1是否可达,如果可达,每次尽量走边a(这样字典序才是最小的)。

  • 预处理好每个节点到n-1是否可达的信息,这样在构造路径的时候好快速选择正确的下一跳。为此,在建图的时候同时记录某个节点的前继节点,然后从节点n-1出发跑一遍BFS即可。
  • 从起点0出发构造路径:
    • 如果当前节点已经是终点了,返回结果;
    • 否则看走a到达的下一跳与终点是否可达,是的话就走a,否则走b
    • 如果选择的下一跳已经被走过,说明构成了一个环,这意味着字典序最小的字符串无限长。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <set>
#include <queue>

using namespace std;

int n;

void solve(vector<int>& a, vector<int>& b, vector<vector<bool>>& adj, vector<vector<int>>& prevs)
{
    int s = 0, t = n - 1;
    vector<bool> can(n, false);
    can[n-1] = true;
    // BFS
    queue<int> q;
    q.push(t);
    while (!q.empty()) {
        int cur = q.front(); q.pop();
        for (int i = 0; i < prevs[cur].size(); ++i) {
            if (!can[prevs[cur][i]]) {
                can[prevs[cur][i]] = true;
                q.push( prevs[cur][i] );
            }
        }
    }

    // go from s
    int cur = s;
    if (!can[cur]) {
        cout << "No solution!" << endl;
        return;
    }
    vector<bool> vis(n, false);
    vis[cur] = true;
    string res = "";
    while (cur != t) {
        for (int i = 0; i < adj[cur].size(); ++i) {
            //pair<char, int> p = adj[cur][i];
            bool select = adj[cur][i];
            int to;
            char select_;
            if (select == true) {
                to = cur + a[cur];
                select_ = 'a';
            } else {
                to = cur + b[cur];
                select_ = 'b';
            }
            if (to == t) {
                res += select_;
                cout << res << endl;
                return;
            }
            if (can[to]) {
                if (vis[to]) {
                    cout << "Infinity!" << endl;
                    return;
                }
                vis[to] = true;  // 一开始由于这句没加上,导致爆空间,浪费了四次提交机会。。
                res += select_;
                cur = to;
                break;
            }
        }
    }
}

int main()
{
    cin >> n;

    vector<int> a(n);
    vector<int> b(n);
    vector<vector<bool>> adj(n);
    vector<vector<int>> prevs(n);

    for (int i = 0; i < n; ++i) {
        scanf("%d", &a[i]);
        int to = i + a[i];
        if (to >= 0 && to < n) {
            adj[i].push_back(true);
            prevs[to].push_back(i);
        }
    }
    for (int i = 0; i < n; ++i) {
        scanf("%d", &b[i]);
        int to = i + b[i];
        if (to >= 0 && to < n) {
            adj[i].push_back(false);
            prevs[to].push_back(i);
        }
    }
    solve(a, b, adj, prevs);
    return 0;
}

数码

https://www.nowcoder.com/question/next?pid=5513596&qid=104907&tid=8742003

  • 首先,计算1~n的结果,则区间[l, r]的结果等于[1, r]减去[1, l-1]
  • 核心是计算最高位为i (1 <= i <= 9)的约数在[1, n]出现的次数
  • i = 2为例,ans[2] = cnt(2) + cnt(20) + cnt(21) + ... + cnt(29) + ... + cnt(200) + cnt(201) + ... + cnt(不超过n的最高位为2的最大的数), 其中cnt(x)表示[1,n]中约数为某个数x的数量,它很好计算:cnt(x) = n / x
  • 那么最高位为i时,分别统计长度为1,2,...的数:长度为1是[i,(i+1)-1]; 长度为2是[i*10, (i+1)*10-1]; 长度为3是[i*10^2, (i+1)*10^2-1],以此类推,长度为j[i*10^(j-1), (i+1)*10^(j-1)-1]
  • 于是ans[i] = sum(cnt(x) for x in range(i, i+1)) + sum(cnt(x) for x in range(i*10, (i+1)*10)) + sum(cnt(x) for x in range(i*10^2, (i+1)*10^2)) + ...
  • 但是当n很大时(比如n >= 10^7),枚举量会很大
  • 长度为j时的数量为sum(cnt(x) for x in range(i*10^(j-1)), (i+1)*10^(j-1)) = n/low + n/(low+1) + ... + n/high,其中low = i*10^(j-1), high = (i+1)*10^(j-1) - 1. 注意到随着x的增加,n / x是单调不增的,且当j >= 6时,最大的项n/low = n / (i*10^(j-1)) <= n / (i*10^5) <= 10^4 / i <= 10^4,可见这么多项的值是不多的(都是离散量),最多也就10000个,这就意味着我们可以换个思路-->去枚举每一项值的范围,然后在{n/low, n/(low+1) ... n/high}中找等于该值的项的数量是多少。至于如何找,其实对每一个值可以O(1)找出来,如果对细节处理没把握,保险起见还可以二分查找-->由于这个数列是单调的,分别找到等于这个值的最左边的项和最右边的项即可
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <set>
#include <queue>

using namespace std;

long long l, r;

long long compute_single(long long low, long long high, long long n)
{
    // compute: n/low + n/(low+1) + ... + n/(high)
    if (low > high) return 0;
    long long ret = 0;
    if (high - low <= 1e5) {
        for (long long i = low; i <= high; ++i) {
            ret += (n / i);
        }
    } else {
        long long start = n / high, end = n / low;
        for (long long x = start; x <= end; ++x) {  // how many n/i equals to x
            long long lb = 0, ub = -1;
            // binary search for lb and ub
            // lb
            long long left = low, right = high, mid;
            while (left + 1 < right) {
                mid = left + (right - left) / 2;
                if (n / mid <= x) right = mid;
                else left = mid;
            }
            if (n / left == x) lb = left;
            else if (n / right == x) lb = right;
            // ub
            left = low; right = high;
            while (left + 1 < right) {
                mid = left + (right - left) / 2;
                if (n / mid >= x) left = mid;
                else right = mid;
            }
            if (n / right == x) ub = right;
            else if (n / left == x) ub = left;
            //cout << lb << " " << ub << " " << x << endl;

            ret += (ub - lb + 1) * x;
        }
    }
    return ret;
}

void compute(long long n, vector<long long>& ans)
{
    if (n == 0) return;
    for (int i = 1; i <= 9; ++i) {  // digit: i
        long long base = 1;
        for (int j = 0; j <= 9; ++j) {  // j+1: the length of number with i the highest digit
            long long low = i * base, high = (i + 1) * base - 1;
            high = min(high, n);
            long long cur = compute_single(low, high, n);
            ans[i] += cur;
            if (high >= n) break;
            base *= 10;
        }
    }
}

void solve()
{
    vector<long long> ans_l(10, 0);
    vector<long long> ans_r(10, 0);
    compute(r, ans_r);
    compute(l-1, ans_l);
    for (int i = 1; i <= 9; ++i) {
        cout << (ans_r[i] - ans_l[i]) << endl;
    }
}

int main()
{
    cin >> l >> r;
    solve();
    return 0;
}

posted @ 2017-06-17 22:58  mioopoi  阅读(336)  评论(0编辑  收藏  举报