UOJ#228.基础数据结构练习题

题目链接

  这个题目就是让我们实现三个操作1.区间加,2.区间开方,3.区间求和。可以用势能线段树来写,也可以用分块来写。我们就用分块来写这个题。
  我们先对整个序列进行预处理,将整个序列分成多个块

for (int i = 1; i <= n; i ++ ) std::cin >> a[i]; int lim = sqrt(n); for (int i = 1; i <= lim; i ++ ) bln[i] = (i - 1) * lim + 1, brn[i] = i * lim; if (brn[lim] < n) lim ++, bln[lim] = brn[lim - 1] + 1, brn[lim] = n; for (int i = 1; i <= lim; i ++ ) for (int j = bln[i]; j <= brn[i]; j ++ ) belong[j] = i, update(i);

  由于本题不同于单单只进行区间加的操作或者区间开方的操作,如果我们仅仅只有sqrtfree来维护这个区间能否继续暴力开方的话,是可以被卡掉的,如果我们一直开方开到1之后,再进行一次区间加的操作,把所有的数都加到105,那么就是出题人在O(1)的时间复杂度下,让我们继续进行O(logV)次暴力,但是每一次的暴力复杂度又是O(n)的,所以很容易就被出题人卡时间了。如果只是单纯的区间开方不进行区间加的话,可以在O(nlogV+qn)的复杂度下完成区间开方。所以本题我们要多用mx[i]mn[i]来维护区间最值,在开方的时候我们只需要去判断最大值和最小值是否相等,如果相等的话,我们计算开方前后的变化量是多少,然后我们对整块进行开方操作就简化成了对整块进行一次区间加法,所有的数都加上一个mxmx,这样就可以实现O(1)的时间复杂度下对整块进行开方的操作。采用区间最大值和最小值来实现区间开方的话,出题人想卡的话,就要用O(nB)次操作去破坏所有块的最大值最小值的性质,这个时候我们的开方操作就会从O(1)退化为O(n),所以采用这个方法的话,出题人卡我们的代价就会变大。

维护区间最值操作

  其实就是将那个整块的标记先释放,然后重新求出summx[bel]mn[bel]

auto update = [&] (int id) { for (int i = bln[id]; i <= brn[id]; i ++ ) a[i] += tag[id]; sum[id] = 0, mx[id] = 0, mn[id] = inf; for (int i = bln[id]; i <= brn[id]; i ++ ) mx[id] = std::max(mx[id], a[i]), mn[id] = std::min(mn[id], a[i]), sum[id] += a[i]; tag[id] = 0; };

操作一:区间加法

  我们用tagsum来维护这个区间的懒惰标记和整块的总和,并且才实现完区间加法的时候,要时刻更新我们的summx,mn,,每一次都要及时的将tag懒惰标记向下释放,为的是维护最大值和最小值。

auto change = [&] (int l, int r, i64 val) { if (belong[l] == belong[r]) { for (int i = l, bel = belong[l]; i <= r; i ++ ) a[i] += val; update(belong[l]); return ; } for (int i = belong[l] + 1; i < belong[r]; i ++ ) tag[i] += val, mx[i] += val, mn[i] += val, sum[i] += 1ll * (brn[i] - bln[i] + 1) * val; for (int i = l, bel = belong[l]; i <= brn[bel]; i ++ ) a[i] += val; for (int i = bln[belong[r]], bel = belong[r]; i <= r; i ++ ) a[i] += val; update(belong[l]), update(belong[r]); };

操作二:区间开方

  判断区间最大值和区间最小值是否相等,从而来选择我们这个时候对区间开方的操作是暴力开方还是采用类区间加法来实现区间开方。

auto sqrtop = [&] (int l, int r) -> void { if (belong[l] == belong[r]) { update(belong[l]); for (int i = l, bel = belong[l]; i <= r; i ++ ) a[i] = i64(sqrt(a[i]) + 1E-9); update(belong[l]); return ; } for (int i = belong[l] + 1; i < belong[r]; i ++ ) { if (mx[i] == mn[i]) { const i64 dlt = mx[i] - sqrtv(mx[i]); tag[i] -= dlt, mx[i] -= dlt, mn[i] -= dlt, sum[i] -= (brn[i] - bln[i] + 1) * dlt; } else { const int sv = sqrtv(mx[i]); if (i64(sv) * sv == mx[i] && mx[i] - mn[i] == 1) { const i64 dlt = mx[i] - sv; tag[i] -= dlt, mx[i] -= dlt, mn[i] -= dlt, sum[i] -= (brn[i] - bln[i] + 1) * dlt; } else { for (int id = brn[i]; id >= bln[i]; --id) SQRT(a[id] += tag[i]); tag[i] = 0, update(i); } } } update(belong[l]); for (int i = l, bel = belong[l]; i <= brn[bel]; i ++ ) a[i] = i64(sqrt(a[i]) + 1E-9); update(belong[l]); update(belong[r]); for (int i = bln[belong[r]], bel = belong[r]; i <= r; i ++ ) a[i] = i64(sqrt(a[i]) + 1E-9); update(belong[r]); };

操作三:区间求和

  区间求和操作就是很正常的操作了。

auto query = [&] (int l, int r) -> i64 { i64 ans = 0; if (belong[l] == belong[r]) { for (int i = l, bel = belong[l]; i <= r; i ++ ) ans += a[i] + tag[bel]; return ans; } for (int i = belong[l] + 1; i < belong[r]; i ++ ) ans += sum[i]; for (int i = l, bel = belong[l]; i <= brn[bel]; i ++ ) ans += a[i] + tag[bel]; for (int i = bln[belong[r]], bel = belong[r]; i <= r; i ++ ) ans += a[i] + tag[bel]; return ans; };

__EOF__

本文作者HoneyGrey
本文链接https://www.cnblogs.com/Haven-/p/16200172.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   浅渊  阅读(133)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示