链接
题意:
给出一个序列 a a a,要求求出一个单调递增的下标序列 b b b,使得 a n s = a b 1 − a b 2 + a b 3 − a b 4 + ⋯ ans=a_{b_1}-a_{b_2}+a_{b_3}-a_{b_4}+\cdots ans=ab1−ab2+ab3−ab4+⋯最大,输出这个最大值。
接下来有
q
q
q个操作,每个操作为一个二元组
(
l
,
r
)
(l,r)
(l,r),交换
a
l
a_l
al 与
a
r
a_r
ar 。求出交换后最大的
a
n
s
ans
ans。
多组数据。
注意:每组数据读入时先是
n
n
n 和
q
q
q,之后是
n
n
n 个元素
a
i
a_i
ai ,再之后是
q
q
q 个操作
l
i
l_i
li与
r
i
r_i
ri 。
分析:
首先我们通过C1可以知道我们如果取的最大值,就是没有q次交换。
直接说结论:如果
a
[
i
]
>
a
[
i
+
1
]
a[i]>a[i+1]
a[i]>a[i+1]ans+=a[i]-a[i+1]
然后我们看到这个地方,我们还要进行q次交换,
l
,
r
l,r
l,r交换,我们看看交换前他们两个地方的代价, a x b … c y d…, 然后我们看他们的代价,a[l]=x,a[r]=y;
- l: 如果a>x ,那么他对答案的贡献:a-x,
如果x>b,他对答案的贡献是x-b; - r: 如果c>y并且c不是x这个位置,因为如果是我们上面已经计算过一次了。他对答案的贡献是:c-y
如果y>d,他对答案的贡献是y-d;
我们把这些没有交换前的贡献减去,然后将交换后新生成的贡献加上即可。
新生成的:与上面一样。
还有线段树维护的方法。
ll n,m;
ll a[maxn],ans;
void solve()
{
cin>>n>>m;
a[0]=0,a[n+1]=0;
ans=0;
for(int i = 1; i <= n; i++){
scanf("%lld",&a[i]);
}
for(int i= 0;i<=n;i ++){
if(a[i]>a[i+1]) ans+=a[i]-a[i+1];
}
printf("%lld\n",ans);
while(m--){
ll l,r;
scanf("%lld%lld",&l,&r);
if(a[l-1]>a[l]) ans-=a[l-1]-a[l];///减去左端贡献
if(a[l]>a[l+1]) ans-=a[l]-a[l+1];///减去右端的贡献
if(a[r-1]>a[r]&&r-1!=l) ans-=a[r-1]-a[r];
if(a[r]>a[r+1]) ans-=a[r]-a[r+1];
swap(a[l],a[r]);
if(a[l-1]>a[l]) ans+=a[l-1]-a[l];///加上左端贡献
if(a[l]>a[l+1]) ans+=a[l]-a[l+1];///加上右端的贡献
if(a[r-1]>a[r]&&r-1!=l) ans+=a[r-1]-a[r];
if(a[r]>a[r+1]) ans+=a[r]-a[r+1];
printf("%lld\n",ans);
}
}