最开始的想法很简单,观察这个正负交替的式子可以发现可以这样配对
a
b
1
+
(
a
b
3
−
a
b
2
)
+
(
a
b
5
−
a
b
4
)
+
.
.
.
+
(
a
b
k
−
a
b
k
−
1
)
\mathbf{a_{b_1}+(a_{b_3}-a_{b_2})+(a_{b_5}-a_{b_4})+...+(a_{b_{k}}-a_{b_{k-1}})}
ab1+(ab3−ab2)+(ab5−ab4)+...+(abk−abk−1)。显然括号中的值是大于等于0的。如果把数组中每个元素用点表示
(
i
,
a
[
i
]
)
\mathbf{(i,a[i])}
(i,a[i]),那么一定会呈现出如下图1的形式:
观察这个图,容易发现
a
b
1
=
①
,
a
b
2
=
②
.
.
.
\mathbf{a_{b_1}=①,a_{b_2}=②...}
ab1=①,ab2=②...,这些元素都具有一个共性,那就是处于一个转折点处。但这个想法很初步,并且不太容易解决动态修改的问题。
尝试着进一步转化,我们发现在枚举i的过程中,其实只要满足
a
[
i
]
−
a
[
i
−
1
]
>
=
0
\mathbf{a[i]-a[i-1]>=0}
a[i]−a[i−1]>=0我们就加到ans里去即可。对于
a
1
\mathbf{a_1}
a1而言,我们在它前面添加一个元素
a
0
=
0
\mathbf{a_0=0}
a0=0即可。如果仔细想一下就会发现这样做是等价于上图的计算方式的。还可以把这个方法写成一个数学表达式,即
a
n
s
=
∑
i
=
1
n
m
a
x
{
0
,
a
[
i
]
−
a
[
i
−
1
]
}
\mathbf{ans=\sum_{i=1}^{n}max\{0,a[i]-a[i-1]\}}
ans=∑i=1nmax{0,a[i]−a[i−1]}那么每个元素的贡献就非常单纯了,也就是
m
a
x
{
0
,
a
[
i
]
−
a
[
i
]
−
a
[
i
−
1
]
}
\mathbf{max\{0,a[i]-a[i]-a[i-1]\}}
max{0,a[i]−a[i]−a[i−1]},如果修改的话就可以直接减去该元素原来的贡献,然后加上该元素更新后的贡献。注意对于
r
−
l
≤
1
\mathbf{r-l\le 1}
r−l≤1的情况需要特判一下。
int n,a[maxn];
void add(int x,ll &ans,bool fg){//加上一个元素的贡献
if(!fg)ans+=max(0,a[x]-a[x-1]);
if(x!=n)ans+=max(0,a[x+1]-a[x]);
}
void del(int x,ll &ans,bool fg){//减去一个元素的贡献
if(!fg)ans-=max(0,a[x]-a[x-1]);
if(x!=n)ans-=max(0,a[x+1]-a[x]);
}
int main(){
int t;
rd(&t);
while(t--){
int q;
rd(&n,&q);
ll ans=0;
FOR(i,1,n+1){
rd(&a[i]);
ans+=max(0,a[i]-a[i-1]);
}
wrn(ans);
while(q--){
int l,r;
rd(&l,&r);
if(l==r){//特判
wrn(ans);
continue;
}
if(l+1==r){//特判
del(l,ans,false);
del(r,ans,true);
swap(a[l],a[r]);
add(l,ans,false);
add(r,ans,true);
}else{
del(l,ans,false);//减去旧的l对ans的贡献
del(r,ans,false);//减去旧的r对ans的贡献
swap(a[l],a[r]);
add(l,ans,false);//加上新的l对ans的贡献
add(r,ans,false);//加上新的r对ans的贡献
}
wrn(ans);
}
}
}