ZOJ - 3940 第十三届浙江省赛 E Modulo Query 想法

题意

给一个数组a,让0到M范围的数字X去依次取模ai,询问q次,有多少个X,计算结果等于询问yi。

https://zoj.pintia.cn/problem-sets/91827364500/problems/91827370044

分析

给大佬博客引流

假如 [ 0 , M ] ( 1 ) [0,M](1) [0,M](1)表示一个1到M区间,每个元素i只有一个元素映射到自己(比如5%100 == 5)。那么取模操作就可以表示成这样。

{ [ 0 , M ] ( 1 ) % A i } → { [ 0 , A i ] ( M / A i ) } + { [ 0 , M % A i ] ( 1 ) } \{ [0,M](1) \% A_i \} \rightarrow \{ [0,A_i] (M / A_i) \} + \{ [0, M \% A_i] (1) \} {[0,M](1)%Ai}{[0,Ai](M/Ai)}+{[0,M%Ai](1)}

这样每个区间单独地简单计数就可以了。

具体实现步骤:

  • 用map的key记右边界,val记个数。
  • 初始状态,在map里插入一个
  • 每次拿到一个ai,遍历查询所有key>=ai的元素,处理这些区间的拆分,

这里要用到map的迭代器与删改操作。
map的迭代器是有空间回收的,也就是说如果你删除了这个迭代器指向的元素,
那么这个迭代器也就失效了。
建议不熟的话可以写笨一点:
auto et = it;
it = next(it);
mp.erase(er);
这题其实还有一点特殊性,修改的元素必定在当前位置的前面,
有时候插到后面还会有难以预料的问题。

  • 反向遍历map,类似做一个后缀和。(可以不用,但我觉得查询复杂度会多一点)
  • 每次询问只要查询大于等于qi的key,即表示映射到qi的数字的元素个数。

感性猜测复杂度不会很高。
理性推敲应该是 O ( N l o g 2 N ) O(Nlog^2N) O(Nlog2N)。(一个log来自二分,一个log来自map)

单例代码

map<int, ll> tree;

void solve(int kaseId = -1) {
    int n, m, q;
    cin >> n >> m;
    tree.clear();
    tree.emplace(m, 1);

    for (int i = 1, ai; i <= n; ++i) {
        cin >> ai; // ai >= 1
        for (auto it = tree.lower_bound(ai); it != tree.end();) {
            int key = it->first;
            ll val = it->second;

            tree.erase(it++);
            tree[ai - 1] += val * (key / ai);
            tree[key % ai] += val;
        }
    }

    ll suf = 0;
    for (auto it = tree.rbegin(); it != tree.rend(); ++it) {
        it->second += suf;
        suf = it->second;
    }

    cin >> q;
    ll ans = 0;
    for (int i = 1, qi; i <= q; ++i) {
        cin >> qi;
        ans = (ans + tree.lower_bound(qi)->second * i) % MOD;
    }

    cout << ans << endl;
}

你可能感兴趣的:(STL,算法,ZOJ,3940)