20220723N work 一个trick线段树
从未见过这种 trick 线段树,还是菜了。
指正一个题面错误:客户愿意花费 \(L_i-k*v_i\) 的钱给工厂

\(1\leq n,m\leq 2*10^5,v_i\leq 10^5\)
如果本题要求第 0 天的收益。那么很简单,我只需要将 \(v_i\) 按照从大到小的顺序完成任务就可以了。
但是现在带 \(m\) 次修改,我们必须想一个快一点的办法。
我们可以建立一棵权值(用其 \(v_i\) )线段树。我们每个任务就是想要知道有多少个大于 \(v_i\) 的任务,这些任务就是需要排在它前面的任务。
用线段树维护后缀和?似乎不太行,但是我们可以考虑对于 \(i\) 任务与 \(j\) 任务。我们可以在 \((i,j)\) 的 LCA 处维护答案,这样我们就可以区分大小。
#include <bits/stdc++.h>
#define ls num << 1
#define rs num << 1 | 1
using namespace std;
template <typename T>inline void read(T& t){t=0; register char ch=getchar(); register int fflag=1;while(!('0'<=ch&&ch<='9')) {if(ch=='-') fflag=-1;ch=getchar();}while(('0'<=ch&&ch<='9')){t=t*10+ch-'0'; ch=getchar();} t*=fflag;}
template <typename T,typename... Args> inline void read(T& t, Args&... args) {read(t);read(args...);}
const int N = 2e5 + 10, inf = 0x3f3f3f3f;
typedef long long ll;
ll sz[N << 2], n, m, a[N], b[N];
ll Sum = 0, sum[N << 2], ans[N << 2];
void pushup(int num) {
sum[num] = sum[ls] + sum[rs];
sz[num] = sz[ls] + sz[rs];
ans[num] = ans[ls] + ans[rs] + sz[rs] * sum[ls];
}
void insert(int num, int l, int r, int x, int y) {
if(l == r) {
if(y == 1) {
sz[num] ++;
sum[num] += x;
ans[num] += sz[num] * x;
} else {
ans[num] -= sz[num] * x;
sz[num] --;
sum[num] -= x;
}
return;
}
int mid = (l + r) >> 1;
if(x <= mid) insert(ls, l, mid, x, y);
else insert(rs, mid + 1, r, x, y);
pushup(num);
}
int main() {
freopen("work.in", "r", stdin);
freopen("work.out", "w", stdout);
read(n, m);
for(int i = 1; i <= n; ++i) read(a[i], b[i]), Sum += a[i], insert(1, 1, N, b[i], 1);
cout << Sum - ans[1] << endl;
while(m--) {
ll A, B, C;
read(A, B, C);
Sum += B;
Sum -= a[A];
a[A] = B;
insert(1, 1, N, b[A], -1);
b[A] = C;
insert(1, 1, N, b[A], 1);
cout << Sum - ans[1] << endl;
}
return 0;
}
// \sigma_{i=1}^{n} i * v[i]

浙公网安备 33010602011771号