题意
给一个数组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里插入一个<m,1>
- 每次拿到一个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;
}