题解:CF1946F Nobody is needed
Nobody is needed
推销我的博客园。
题意
多组数据。
给定一个长度为
-
-
### 数据范围 -
-
-
## 思路
考虑使用 dp,令
那么转移呢,很显然若是写收集形则
若是写扩散形,又不便于确定对于每组询问的答案。
可以考虑将拓扑序转为
那么考虑怎么更新 dp 数组,正如上文所说,每次发生修改的只有
为了保证查询时答案没有超出
详细见代码。
复杂度
- 时间:
O(q \log n + n \log^2 n) 。 - 空间:
O(n + q) 。
Code
#include <iostream>
#include <vector>
#define _1 (__int128)1
using namespace std;
using ll = long long;
const int N = 1e6 + 10;
inline int lowbit (int x) {
return x & -x;
}
int T, n, q, a[N], b[N];
ll ans[N], tr[N], dp[N];
vector<pair<int, int>> qry[N];
void modify (int x, int y) {
for (int i = x; i <= n; i += lowbit(i)) tr[i] += y;
}
ll Query (int x) {
ll ret = 0;
for (int i = x; i; i -= lowbit(i)) ret += tr[i];
return ret;
}
void Solve () {
cin >> n >> q;
for (int i = 1; i <= n; i++)
cin >> a[i], b[a[i]] = i, tr[i] = 0, qry[i] = vector<pair<int, int>> (); // 多测要清空,b[i] 用于记录 i 在 a 中对应的下标
for (int i = 1, l, r; i <= q; i++)
cin >> l >> r, qry[l].push_back({r, i}); // 离线处理询问
for (int i = n; i; i--) {
dp[a[i]] = 1; // 初始状态
for (int j = a[i]; j <= n; j += a[i])
if (b[j] >= b[a[i]]) // 将两种转移合并可以这样写
for (int k = j * 2; k <= n; k += j) // 注意是 k += j 而不是 k += a[i]
if (b[k] > b[j])
dp[k] += dp[j];
for (int j = a[i]; j <= n; j += a[i])
modify(b[j], dp[j]), dp[j] = 0; // 用树状数组维护 dp 和,dp 要清空
for (auto [j, id] : qry[i])
ans[id] = Query(j) - Query(i - 1); // 差分处理答案
}
for (int i = 1; i <= q; i++)
cout << ans[i] << ' ';
}
int main () {
ios::sync_with_stdio(0), cin.tie(0);
// Solve();
for (cin >> T; T--; Solve(), cout << '\n') {}
return 0;
}