CodeForce1485B. Replace and Keep Sorted (*1200)

18 篇文章 0 订阅

题目链接

CodeForce1485B. Replace and Keep Sorted

思路

一开始没有思路,只想到枚举每个替换的值。但这样是 O ( n 2 ) O(n^2) O(n2)算法,会超时。

记输入的数组为 a [ n ] , i ∈ [ 1 , n ] a[n], i\in[1,n] a[n],i[1,n],即与题目中角标保持一致。

发现每个可替换的数必须仍然在区间之间。因此可以构造 d i f f [ i ] diff[i] diff[i]
d i f f [ i ] = a [ i + 1 ] − a [ i − 1 ] − 2 diff[i]=a[i+1]-a[i-1]-2 diff[i]=a[i+1]a[i1]2
其中 d i f f [ i ] ( 1 ≤ i ≤ n ) diff[i]\quad(1\le i \le n) diff[i](1in)对应每个元素位于区间内部时的可替换方案数。

由于当前位置i必须大于前一个元素而小于后一个元素。
可取的取值个数是 ( a [ i − 1 ] , a [ i + 1 ] ) (a[i-1],a[i+1]) (a[i1],a[i+1]),再除去它自身。
故有 d i f f [ i ] = ( a [ i + 1 ] − a [ i − 1 ] ) − 2 diff[i]=(a[i+1]-a[i-1])-2 diff[i]=(a[i+1]a[i1])2

  • 为了方便观察,令 a [ 0 ] = 0 , a [ n + 1 ] = k + 1 a[0]=0,a[n + 1]=k + 1 a[0]=0,a[n+1]=k+1,这样就方便处理边界情况,避免越界。
  • 但注意到存在 a [ r ] < k a[r] < k a[r]<k以及 a [ l ] > 1 a[l] > 1 a[l]>1的情况。所以对于查询的端点 l , r l,r l,r只有单侧限制而不是双侧限制
  • 记查询区间 [ l , r ] [l,r] [l,r]的方案数为 P l , r P_{l,r} Pl,r,于是 P l , r = ∑ i = l + 1 r − 1 d i f f [ i ] + P l + P r P_{l,r}=\sum_{i=l+1}^{r-1} diff[i]+P_l+P_r Pl,r=i=l+1r1diff[i]+Pl+Pr
    其中 P l P_l Pl代表左端点 l l l的方案总数, P r P_r Pr代表右端点 r r r的方案总数。

处理端点

对于查询端点 l l l,只要位置 l l l替换的值小于 a [ l + 1 ] a[l+1] a[l+1]就可以了,再除去 a [ l ] a[l] a[l],故有 a [ l + 1 ] − 2 a[l+1]-2 a[l+1]2个。
对于查询端点 r r r,只要位置 r r r替换的值大于 a [ r − 1 ] a[r-1] a[r1]就可以了,再除去 a [ r ] a[r] a[r],故有 k − a [ r − 1 ] − 1 k - a[r-1] - 1 ka[r1]1个。
至此拼凑以上三部分即得到总的方案数。

k − a [ r − 1 ] − 1 k - a[r-1] - 1 ka[r1]1一定非负。
考虑 a [ r − 1 ] = k a[r-1]=k a[r1]=k的情况,此时 k − a [ r − 1 ] − 1 = − 1 < 0 k - a[r-1] - 1=-1<0 ka[r1]1=1<0
但由题设知 a r − 1 < a r < k a_{r-1}<a_r<k ar1<ar<k,故符合题意的 a r a_r ar不存在,矛盾。
a [ r − 1 ] < k a[r-1]<k a[r1]<k,则必有 k − a [ r − 1 ] − 1 ≥ 0 k - a[r-1] - 1\ge0 ka[r1]10
对于 a [ l + 1 ] − 2 ≥ 0 a[l+1]-2\ge0 a[l+1]20同理。

代码

#include<bits/stdc++.h>

using namespace std;
int n, q, k;
int l, r;
typedef long long LL;

int main() {
    cin >> n >> q >> k;
    vector<LL> a(n + 2);
    vector<LL> diff(n + 1), preSum(n + 2); // diff[i]表示a[i] (其中 l < i < r)进行替换有多少种选择

    a[0] = 0;         // pad 0, k + 1
    a[n + 1] = k + 1;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    for (int i = 1; i <= n; ++i)
        diff[i] = a[i + 1] - a[i - 1] - 2; // 除去他自己和两边的端点

    for (int i = 1; i <= n; ++i)
        preSum[i + 1] = preSum[i] + diff[i];
    while (q--) {
        cin >> l >> r;
        auto ans = preSum[r] - preSum[l + 1] // [l+1,r-1]位于内部,则根据diff的前缀和进行计算
                   + (k - a[r - 1] - 1) // 比a[r-1]大的,除去a[r],共有k - a[r-1] - 1个
                   + a[l + 1] - 2;      // a[l+1]和a[l]不能取,故-2
        printf("%lld\n", ans);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值