【ybtoj高效进阶 21290】头文件 D(平衡规划)(线段树)

头文件 D

题目链接:ybtoj高效进阶 21290

题目大意

给你一个序列,下标从 1 到 n。
然后有两类操作,要么是给出 k,l,r,x 把所有下标 %k 的值在 l~r 之间的位置都加上 x。
要么是区间求和。

思路

发现没有什么比较好的数据结构能实现。

考虑观察它修改的性质。
如果它 k 很大,那修改的段就很小,可以直接暴力枚举每个段。
如果它 k 很小,那它修改段很多,但是每段很小,可以暴力用前缀和的方法统计。

所以我们考虑使用平衡规划。
自然分成 knk>n 两个部分。
第一个部分,我们直接 fi,jk=i 的修改,它第 j 个位置是加了 fi,j
然后你在每次搞了就重新计算前缀和 gi,j
然后就可以很快的统计了。

第二个部分,直接暴力找每一段线段树赋值,然后插叙就直接查询线段树即可。

代码

#include<cmath> #include<cstdio> #include<iostream> #define ll long long using namespace std; ll n, m, op, x, y, k, z; ll f[501][501], g[501][501]; ll a[200001], ans, sum[501]; struct XD_tree {//线段树 ll v[200002 << 2], lzy[200002 << 2]; void up(ll now) { v[now] = v[now << 1] + v[now << 1 | 1]; } void down(ll now, ll l, ll r) { if (!lzy[now]) return ; ll mid = (l + r) >> 1; v[now << 1] += lzy[now] * (mid - l + 1); v[now << 1 | 1] += lzy[now] * (r - mid); lzy[now << 1] += lzy[now]; lzy[now << 1 | 1] += lzy[now]; lzy[now] = 0; } void build(ll now, ll l, ll r) { if (l == r) { v[now] = a[l]; return ; } ll mid = (l + r) >> 1; build(now << 1, l, mid); build(now << 1 | 1, mid + 1, r); up(now); } void insert(ll now, ll l, ll r, ll L, ll R, ll va) { if (L <= l && r <= R) { lzy[now] += va; v[now] += 1ll * va * (r - l + 1); return ; } down(now, l, r); ll mid = (l + r) >> 1; if (L <= mid) insert(now << 1, l, mid, L, R, va); if (mid < R) insert(now << 1 | 1, mid + 1, r, L, R, va); up(now); } ll query(ll now, ll l, ll r, ll L, ll R) { if (L <= l && r <= R) return v[now]; down(now, l, r); ll mid = (l + r) >> 1, re = 0; if (L <= mid) re += query(now << 1, l, mid, L, R); if (mid < R) re += query(now << 1 | 1, mid + 1, r, L, R); return re; } }T; int main() { // freopen("sorrow.in", "r", stdin); // freopen("sorrow.out", "w", stdout); scanf("%lld %lld", &n, &m); for (ll i = 1; i <= n; i++) scanf("%lld", &a[i]); T.build(1, 1, n); ll lim = sqrt(n); while (m--) { scanf("%lld", &op); if (op == 1) { scanf("%lld %lld %lld %lld", &k, &x, &y, &z); if (k <= lim) {//小的直接暴力前缀和 for (ll i = x; i <= y; i++) { f[k][i] += z; } g[k][0] = f[k][0]; for (int i = 1; i < k; i++) g[k][i] = g[k][i - 1] + f[k][i]; sum[k] += (y - x + 1) * z; } else { for (ll i = 0; i + x <= n; i += k) T.insert(1, 1, n, i + x, min(n, i + y), z); } } if (op == 2) { scanf("%lld %lld", &x, &y); ans = T.query(1, 1, n, x, y); for (ll i = 1; i <= lim; i++) { ll nm1 = x / i, nm2 = y / i; ans += sum[i] * (nm2 - nm1); ans += g[i][y % i];//加上后面的一节,减去前面多算的一节 if (x % i != 0) ans -= g[i][x % i - 1]; } printf("%lld\n", ans); } } return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/YBTOJ_GXJJ_21290.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(31)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示