P9388 [THUPC 2023 决赛] 先人类的人类选别

[THUPC 2023 决赛] 先人类的人类选别

Description

https://www.luogu.com.cn/problem/P9388

Solution

观察到操作序列一定,操作顺序对答案并没有影响。

将答案拆为 $\sum\limits_{i\le r}a_i-\sum\limits_{i<l}a_i$ ,只需要求出操作后的前缀和即可。

观察到对于一个前缀区间,操作的本质就是将当前操作的所有 $x$ 和 $a_i$ 扔到一个堆里,取最小的前 $q$ 扔给后面。所以只需要快速找到前 $q$ 小之和即可。对于序列和操作分别用主席树和权值线段树,查询两个一起跑。

没有卡常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#define int long long
#define maxn 500005
int n,m;
int a[maxn],suf[maxn],root[maxn],rota,cnt;
struct node {
int ls,rs,sum,siz;
} f[maxn*30];
int Update(int l,int r,int pre,int head) {
int rt=++cnt;
f[rt]=f[pre];
f[rt].siz++;
f[rt].sum+=head;
if (l==r) return rt;
int mid=l+r>>1;
if (head<=mid) f[rt].ls=Update(l,mid,f[rt].ls,head);
else f[rt].rs=Update(mid+1,r,f[rt].rs,head);
return rt;
}
void Update2(int l,int r,int &rt,int head) {
if (!rt) rt=++cnt;
f[rt].siz++,f[rt].sum+=head;
if (l==r) return ;
int mid=l+r>>1;
if (head<=mid) Update2(l,mid,f[rt].ls,head);
else Update2(mid+1,r,f[rt].rs,head);
}
int Query(int l,int r,int rt1,int rt2,int k) {
if (l==r) return k*l;
int mid=l+r>>1,siz=f[f[rt1].ls].siz+f[f[rt2].ls].siz;
if (siz>=k) return Query(l,mid,f[rt1].ls,f[rt2].ls,k);
else return f[f[rt1].ls].sum+f[f[rt2].ls].sum+Query(mid+1,r,f[rt1].rs,f[rt2].rs,k-siz);
}
signed main(void) {
int i,x,l,r,sum=0;
read(n);
read(m);
for (i=1; i<=n; i++) read(a[i]),suf[i]=suf[i-1]+a[i];
for (i=1; i<=n; i++) {
root[i]=Update(1,n,root[i-1],a[i]);
}
for (i=1; i<=m; i++) {
read(x);read(l);read(r);sum+=x;
Update2(1,n,rota,x);
int tmp1=suf[l-1]+sum-Query(1,n,root[l-1],rota,i);
int tmp2=suf[r]+sum-Query(1,n,root[r],rota,i);
printf("%lld\n",tmp2-tmp1);
}
return 0;
}