P7619 [COCI2011-2012#2] RASPORED 题解

我们首先考虑没有修改的情况。

首先,我们假设已经给所有的居民分配好了一个烘焙披萨的方案,并用 posi 代表第 i 位居民的披萨是第 posi 个烘焙的。

这样的话,我们设 Ci 为从第 i 名居民身上获取的小费数额(如果为负数则表示需要向该位居民支付的数额的相反数),那么就会有如下的式子:

Ci=Lij=1posiTj

那么对于 Mirko 收获小费的总和 i=1nCi,我们可以推一下式子:

i=1nCi=i=1n(Lij=1posiTj)=i=1nLii=1nj=1posiTj=i=1nLii=1nTi×(nposi+1)

最后一步是这样推出来的:

对于 Ti 来说,它会在计算所有 posjposij 的时候被计算到,所以总共就是 j=posinTi,即 Ti×(nposi+1)


然后看带修改的。

我们看刚才推出来的式子,可以看出我们能够对于 LT 分开考虑,而事实上我们也就是这样做的。

我们对于一开始没有被修改时的数据计算出一个 suml=i=1nLi,然后再计算出一个 sumt=i=1nTi×(nposi+1)
我们最终输出的数值就是 sumlsumt

假设我们有了一个新的修改,将 (Lx,Tx) 修改为 (Ly,Ty)

对于 suml,其只需要变为 sumlLx+Ly 即可。

对于 sumt,我们可以看成首先从数列里面删除了一个 Tx,然后再插入了一个 Ty;其中 Tx 删除前的位置是 posxTy 删除后的位置是 posy

我们可以将删除和插入分开讨论,也可以只讨论改变位置的元素。

如果分开讨论删除和插入的话,我们的分析过程是这个样子的:

  1. 对于 Ti<Tx,我们的 posi 不会变,但是 n 会因删除而减小 1;而对于 TiTx,我们的 posin 都会减小 1 而最终抵消;对于 Tx,我们需要减去它的贡献。

所以我们的 sumt 在删除 Tx 之后会变成这个样子:

sumtsumtTx×(nposx+1)Ti<TxTi

  1. 对于 Ti<Tx,我们的 posi 不会变,但是 n 会因插入而增大 1;而对于 TiTx,我们的 posin 都会增大 1 而最终抵消;对于 Ty,我们需要加上它的贡献。

所以我们的 sumt 在插入 Ty 之后会变成这个样子:

sumtsumt+Tx×(nposy+1)+Ti<TxTi

如果只讨论改变位置的元素的话,我们的分析过程是这个样子的:

  1. 如果 Tx<Ty,那么对于 {Ti|posi(posx,posy)},其 posi 会减少 1,从而导致 sumt 减少 posi(posx,posy)Ti

  2. 如果 Tx>Ty,那么对于 {Ti|posi(posy,posx)},其 posi 会增加 1,从而导致 sumt 增加 posi(posx,posy)Ti

总的来看,我们的变化量可以看做 Ti<TxTiTi<TxTi

再加上 Ty 的贡献,减去Tx 的贡献,我们推出的式子跟上面的是一样的。


于是我们就需要一种数据结构,支持

  1. 插入和删除元素
  2. 查询小于一个元素的数字个数
  3. 查询小于一个元素的数字之和

树状数组、权值线段树和平衡树均可。

我这里使用的是替罪羊树。

对于不知道替罪羊树的人,我在这里安利一下我的博客,同时也给出OI-Wiki关于替罪羊树的讲解。

替罪羊树虽然比较慢,但是所有的操作时间复杂度均摊之后都是 O(logn) 级别的。

这里粘一下代码:

#include<bits/stdc++.h> using namespace std; #define ll long long const int N = 200010; ll n, m; double alpha = 0.75; struct Scapegoat { int ls, rs; ll w, wn; int s, sz, sd; ll sum; }tr[N]; int cnt, rt; void calc(int p) { tr[p].s = tr[tr[p].ls].s + tr[tr[p].rs].s + 1; tr[p].sz = tr[tr[p].ls].sz + tr[tr[p].rs].sz + tr[p].wn; tr[p].sd = tr[tr[p].ls].sd + tr[tr[p].rs].sd + (tr[p].wn != 0); tr[p].sum = tr[tr[p].ls].sum + tr[tr[p].rs].sum + tr[p].wn * tr[p].w; } bool canrbu(int p) { return tr[p].wn && (alpha * tr[p].s <= ( double )max(tr[tr[p].ls].s, tr[tr[p].rs].s) || ( double )tr[p].sd <= alpha * tr[p].s); }//can rebuild int ldr[N]; void rbuunf(int &ldc, int p) { if(!p)return; rbuunf(ldc, tr[p].ls); if(tr[p].wn)ldr[ldc++] = p; rbuunf(ldc, tr[p].rs); }//rebuild-unfold int rbubld(int l, int r) { if(l >= r)return 0; int mid = (l + r) >> 1; tr[ldr[mid]].ls = rbubld(l, mid); tr[ldr[mid]].rs = rbubld(mid + 1, r); calc(ldr[mid]); return ldr[mid]; }//rebuild-build void rbuild(int &p) { int ldc = 0; rbuunf(ldc, p); p = rbubld(0, ldc); }//rebuild void insert(int &p, ll k) { if(!p) { p = ++cnt; if(!rt)rt = 1; tr[p].w = k; tr[p].ls = tr[p].rs = 0; tr[p].wn = tr[p].s = tr[p].sz = tr[p].sd = 1; tr[p].sum = k; } else { if(tr[p].w == k)tr[p].wn++; else if(tr[p].w < k)insert(tr[p].rs, k); else insert(tr[p].ls, k); calc(p); if(canrbu(p))rbuild(p); } } void loschn(int &p, ll k) { if(!p)return; if(tr[p].w == k) { if(tr[p].wn)tr[p].wn--; } else { if(tr[p].w < k)loschn(tr[p].rs, k); else loschn(tr[p].ls, k); } calc(p); if(canrbu(p))rbuild(p); }//löschen,delete是关键字就不用了 ll uprgtr(int p, ll k) { if(!p) return 0; else if(tr[p].w == k && tr[p].wn) return tr[tr[p].ls].sz; else if(tr[p].w < k) return tr[tr[p].ls].sz + tr[p].wn + uprgtr(tr[p].rs, k); else return uprgtr(tr[p].ls, k); }//相当于是使用greater<>函数排序之后的upper_bound //输出的结果是小于某个元素的数的个数 ll uprsum(int p, ll k) { if(!p) return 0; else if(tr[p].w == k && tr[p].wn) return tr[tr[p].ls].sum; else if(tr[p].w < k) return tr[tr[p].ls].sum + tr[p].wn * tr[p].w + uprsum(tr[p].rs, k); else return uprsum(tr[p].ls, k); }//跟上面差不多,输出的是小于某个元素的数之和 ll suml, sumt; ll l[N], t[N]; int main() { scanf("%lld%lld", &n, &m); int temp[N]; for(int i = 1; i <= n; i++) { scanf("%lld%lld", &l[i], &t[i]); suml += l[i]; insert(rt, t[i]); temp[i] = t[i]; } sort(temp + 1, temp + 1 + n); for(int i = 1; i <= n; i++) sumt += (n - i + 1) * temp[i]; printf("%lld\n", suml - sumt); while(m--) { int a; ll b, c; scanf("%d%lld%lld", &a, &b, &c); suml -= l[a] - b; l[a] = b; ll sum1 = uprsum(rt, t[a]), cnt1 = uprgtr(rt, t[a]); loschn(rt, t[a]); insert(rt, c); ll sum2 = uprsum(rt, c), cnt2 = uprgtr(rt, c); sumt += c * (n - cnt2) + sum2 - t[a] * (n - cnt1) - sum1; t[a] = c; printf("%lld\n", suml - sumt); } return 0; }

__EOF__

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