Description
给定一个长度为 n 的序列 a,有 q 次操作,每次操作交换 a[l],a[r]
,在操作前和每次操作后求出 a 中所有子序列的交替和((s_1-s_2+s_3-s_4+...))的最大值。
Solution
很容易想到线段树维护区间信息,可是这样太暴力了。
发现所谓子序列的交替和,实际上就是在原数列的差分数列中选出一些段,使得他们的和最小/最大。
基于上面这个感性的结论,我们可以将原问题转化为,选取若干个位置 (i),使得 (sum_i max(0,a[i]-a[i-1])) 最大。
显然我们只需要选择那些 (>0) 的部分即可。
设 (f(i)=max(0,a[i]-a[i-1])),则每次修改 (l,r) 最多只会影响到 (l,l+1,r,r+1) 四个位置,我们先减去它们的贡献,修改完毕后,再把他们加回去即可。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1000005;
int n,q,a[N],ans;
int f(int i)
{
if(i>n) return 0;
return max(0ll,a[i]-a[i-1]);
}
int calc(int x,int y)
{
int ans=0;
ans+=f(x);
ans+=f(x+1);
if(y!=x && y!=x+1) ans+=f(y);
if(y+1!=x && y!=x) ans+=f(y+1);
return ans;
}
void solve()
{
cin>>n>>q;
ans=0;
for(int i=1;i<=n+2;i++) a[i]=0;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) ans+=f(i);
cout<<ans<<endl;
while(q--)
{
int l,r;
cin>>l>>r;
ans-=calc(l,r);
swap(a[l],a[r]);
ans+=calc(l,r);
cout<<ans<<endl;
}
}
signed main()
{
ios::sync_with_stdio(false);
int t;
cin>>t;
while(t--)
{
solve();
}
}