我们首先考虑没有修改的情况。
首先,我们假设已经给所有的居民分配好了一个烘焙披萨的方案,并用 posi 代表第 i 位居民的披萨是第 posi 个烘焙的。
这样的话,我们设 Ci 为从第 i 名居民身上获取的小费数额(如果为负数则表示需要向该位居民支付的数额的相反数),那么就会有如下的式子:
Ci=Li−posi∑j=1Tj
那么对于 Mirko 收获小费的总和 ∑ni=1Ci,我们可以推一下式子:
n∑i=1Ci=n∑i=1(Li−posi∑j=1Tj)=n∑i=1Li−n∑i=1posi∑j=1Tj=n∑i=1Li−n∑i=1Ti×(n−posi+1)
最后一步是这样推出来的:
对于 Ti 来说,它会在计算所有 posj≥posi 的 j 的时候被计算到,所以总共就是 ∑nj=posiTi,即 Ti×(n−posi+1)。
然后看带修改的。
我们看刚才推出来的式子,可以看出我们能够对于 L 与 T 分开考虑,而事实上我们也就是这样做的。
我们对于一开始没有被修改时的数据计算出一个 suml=∑ni=1Li,然后再计算出一个 sumt=∑ni=1Ti×(n−posi+1)。
我们最终输出的数值就是 suml−sumt。
假设我们有了一个新的修改,将 (Lx,Tx) 修改为 (Ly,Ty)。
对于 suml,其只需要变为 suml−Lx+Ly 即可。
对于 sumt,我们可以看成首先从数列里面删除了一个 Tx,然后再插入了一个 Ty;其中 Tx 删除前的位置是 posx,Ty 删除后的位置是 posy。
我们可以将删除和插入分开讨论,也可以只讨论改变位置的元素。
如果分开讨论删除和插入的话,我们的分析过程是这个样子的:
- 对于 ∀Ti<Tx,我们的 posi 不会变,但是 n 会因删除而减小 1;而对于 ∀Ti≥Tx,我们的 posi 和 n 都会减小 1 而最终抵消;对于 Tx,我们需要减去它的贡献。
所以我们的 sumt 在删除 Tx 之后会变成这个样子:
sumt→sumt−Tx×(n−posx+1)−∑Ti<TxTi
- 对于 ∀Ti<Tx,我们的 posi 不会变,但是 n 会因插入而增大 1;而对于 ∀Ti≥Tx,我们的 posi 和 n 都会增大 1 而最终抵消;对于 Ty,我们需要加上它的贡献。
所以我们的 sumt 在插入 Ty 之后会变成这个样子:
sumt→sumt+Tx×(n−posy+1)+∑Ti<TxTi
如果只讨论改变位置的元素的话,我们的分析过程是这个样子的:
-
如果 Tx<Ty,那么对于 {Ti|posi∈(posx,posy)},其 posi 会减少 1,从而导致 sumt 减少 ∑posi∈(posx,posy)Ti。
-
如果 Tx>Ty,那么对于 {Ti|posi∈(posy,posx)},其 posi 会增加 1,从而导致 sumt 增加 ∑posi∈(posx,posy)Ti。
总的来看,我们的变化量可以看做 ∑Ti<TxTi−∑Ti<TxTi。
再加上 Ty 的贡献,减去Tx 的贡献,我们推出的式子跟上面的是一样的。
于是我们就需要一种数据结构,支持
- 插入和删除元素
- 查询小于一个元素的数字个数
- 查询小于一个元素的数字之和
树状数组、权值线段树和平衡树均可。
我这里使用的是替罪羊树。
对于不知道替罪羊树的人,我在这里安利一下我的博客,同时也给出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);
}
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);
}
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];
}
void rbuild(int &p)
{
int ldc = 0;
rbuunf(ldc, p);
p = rbubld(0, ldc);
}
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);
}
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);
}
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__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现