CF 1420 C2. Pokémon Army (hard version) (思维|线段树)

链接

题意:

给出一个序列 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=ab1ab2+ab3ab4+最大,输出这个最大值。

接下来有 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);
    }

}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值