[CF1420C2] Pokémon Army (hard version)

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();
    }
}
原文地址:https://www.cnblogs.com/mollnn/p/13884837.html