CF1946F Nobody is needed 题解
Nobody is needed
推销我的洛谷博客。
题意
多组数据。
给定一个长度为
。 。
数据范围
。 是一个排列。 。
思路
考虑使用 dp,令
那么转移呢,很显然若是写收集形则
若是写扩散形,又不便于确定对于每组询问的答案。
可以考虑将拓扑序转为
那么考虑怎么更新 dp 数组,正如上文所说,每次发生修改的只有
为了保证查询时答案没有超出
详细见代码。
复杂度
- 时间:
。 - 空间:
。
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; }
本文作者:wnsyou の blog
本文链接:https://www.cnblogs.com/wnsyou-blog/p/18340587/CF1946F_solution
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步