2023“钉耙编程”中国大学生算法设计超级联赛(1)Easy problem I
题目大意
有一个长度为 n n n的数组 a a a,有 m m m次操作,操作类型有两种:
- 1 l r x j 1 \ l \ r \ x_j 1 l r xj,对于每个 i ∈ [ l , r ] i\in[l,r] i∈[l,r],令 a i = ∣ a i − x j ∣ a_i=|a_i-x_j| ai=∣ai−xj∣
- 2 l r 2 \ l \ r 2 l r,输出 a n s = ∑ i = l r a i ans=\sum\limits_{i=l}^ra_i ans=i=l∑rai
其中 x j ≤ x j + 1 x_j\leq x_{j+1} xj≤xj+1
有 t t t组数据。
1 ≤ t ≤ 5 , 1 ≤ n ≤ 2 × 1 0 5 , 1 ≤ m ≤ 2 × 1 0 5 1\leq t\leq 5,1\leq n\leq 2\times 10^5,1\leq m\leq 2\times 10^5 1≤t≤5,1≤n≤2×105,1≤m≤2×105
题解
我们可以根据绝对值来在线段树上分别维护。
对于 a i ≥ x a_i\geq x ai≥x的情况,直接用线段树维护。对于满足 a i ≥ x a_i\geq x ai≥x的一段, a i = a i − x a_i=a_i-x ai=ai−x,整段减去 x x x即可。
对于 a i < x a_i<x ai<x的情况, a i = x − a i a_i=x-a_i ai=x−ai,相当于将 a i a_i ai取反再加上 x x x。每次改变时 a i a_i ai的数值不变化,只有符号在改变,则在线段树上可以维护符号的正负和要增加的数。
因为 x j ≤ x j + 1 x_j\leq x_{j+1} xj≤xj+1,当 a i < a j a_i<a_j ai<aj时, ∣ a i − x j ∣ ≤ x j ≤ x j + 1 |a_i-x_j|\leq x_j\leq x_{j+1} ∣ai−xj∣≤xj≤xj+1,所以当从 a i ≥ x a_i\geq x ai≥x的状态变为 a i < x a_i<x ai<x的状态之后就不会改变,那么改变的次数是有限的。
转变一个位置的状态的时间复杂度为 O ( l o g n ) O(log n) O(logn),最多会转变 n n n个位置,时间复杂度为 O ( n log n ) O(n\log n) O(nlogn)。操作中除去转变状态以外的部分的时间复杂度为 O ( m log n ) O(m\log n) O(mlogn)。所以总时间复杂度为 O ( n log n + m log n ) O(n\log n+m\log n) O(nlogn+mlogn)。
code
#include<bits/stdc++.h>
#define lc k<<1
#define rc k<<1|1
using namespace std;
int tq,n,m,a[200005];
long long ans;
struct node{
long long sum1,sum2,mn,v,ly1,ly2,ly3;
}tr[1000005];
void up(int k){
tr[k].ly1=tr[k].ly3=0;
tr[k].ly2=1;
tr[k].mn=min(tr[lc].mn,tr[rc].mn);
tr[k].sum1=tr[lc].sum1+tr[rc].sum1;
tr[k].sum2=tr[lc].sum2+tr[rc].sum2;
tr[k].v=tr[lc].v+tr[rc].v;
}
void down(int k,int l,int r){
int mid=l+r>>1;
tr[lc].ly1+=tr[k].ly1;
tr[rc].ly1+=tr[k].ly1;
tr[lc].sum1-=tr[k].ly1*tr[lc].v;
tr[rc].sum1-=tr[k].ly1*tr[rc].v;
tr[lc].mn-=tr[k].ly1;
tr[rc].mn-=tr[k].ly1;
tr[lc].ly2*=tr[k].ly2;
tr[rc].ly2*=tr[k].ly2;
tr[lc].ly3=tr[k].ly3+tr[lc].ly3*tr[k].ly2;
tr[rc].ly3=tr[k].ly3+tr[rc].ly3*tr[k].ly2;
tr[lc].sum2=tr[k].ly3*(mid-l+1-tr[lc].v)+tr[lc].sum2*tr[k].ly2;
tr[rc].sum2=tr[k].ly3*(r-mid-tr[rc].v)+tr[rc].sum2*tr[k].ly2;
tr[k].ly1=tr[k].ly3=0;
tr[k].ly2=1;
}
void build(int k,int l,int r){
if(l==r){
tr[k].ly1=tr[k].ly3=0;
tr[k].ly2=1;
tr[k].mn=tr[k].sum1=a[l];
tr[k].sum2=0;
tr[k].v=1;
return;
}
int mid=l+r>>1;
build(lc,l,mid);
build(rc,mid+1,r);
up(k);
}
void ch(int k,int l,int r,int x,int y,int c){
if(l>=x&&r<=y){
if(tr[k].v){
if(l==r){
if(tr[k].mn<c){
tr[k].sum2=c-tr[k].sum1;
tr[k].sum1=tr[k].v=0;
tr[k].mn=1e18;
}
else{
tr[k].sum1=tr[k].mn=tr[k].sum1-c;
}
}
else{
if(tr[k].mn<c){
down(k,l,r);
int mid=l+r>>1;
ch(lc,l,mid,x,y,c);
ch(rc,mid+1,r,x,y,c);
up(k);
}
else{
tr[k].ly1+=c;
tr[k].mn-=c;
tr[k].sum1-=1ll*c*tr[k].v;
tr[k].ly2*=-1;
tr[k].ly3=c-tr[k].ly3;
tr[k].sum2=1ll*c*(r-l+1-tr[k].v)-tr[k].sum2;
}
}
}
else{
tr[k].ly2*=-1;
tr[k].ly3=c-tr[k].ly3;
tr[k].sum2=1ll*c*(r-l+1)-tr[k].sum2;
}
return;
}
down(k,l,r);
int mid=l+r>>1;
if(x<=mid) ch(lc,l,mid,x,y,c);
if(y>mid) ch(rc,mid+1,r,x,y,c);
up(k);
}
void find(int k,int l,int r,int x,int y){
if(l>=x&&r<=y){
ans+=tr[k].sum1+tr[k].sum2;
return;
}
down(k,l,r);
int mid=l+r>>1;
if(x<=mid) find(lc,l,mid,x,y);
if(y>mid) find(rc,mid+1,r,x,y);
}
int main()
{
scanf("%d",&tq);
while(tq--){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
build(1,1,n);
for(int i=1,tp,l,r,x;i<=m;i++){
scanf("%d",&tp);
if(tp==1){
scanf("%d%d%d",&l,&r,&x);
ch(1,1,n,l,r,x);
}
else{
scanf("%d%d",&l,&r);
ans=0;
find(1,1,n,l,r);
printf("%lld\n",ans);
}
}
}
return 0;
}