AtCoder Regular Contest 077 E - guruguru

题目链接: AtCoder Regular Contest 077 E - guruguru

题目大意

两个按钮, 一个可以使计数器+1(计数器数字从1-m), 当前值为m时, 再+1就变成了1
另一个按钮储存了一个值x, 按一下就从任意值会变成x
n-1次操作, 由一个数组a[n]描述, 第i次操作: 将计数器从a[i]调到a[i+1]
将x设置为某个值, 使得所有操作需要按按钮的次数总和最小, 输出这个最小值
范围:
2n,m105 2 ≤ n , m ≤ 10 5
1aim 1 ≤ a i ≤ m
aiai+1 a i ≠ a i + 1
n,m,ai n , m , a i 都 是 整 数

思路

设置一个数组p, p[i] := 如果x在i位置, 对于所有操作, 使用第二个按钮能够减少的操作次数(相对于只使用第一个按钮)
l = a[i], r = a[i+1], 如果l>r, 令r = r+m, 这样就不用考虑上界的问题了
那么对于每一对l, r, 如果r-l<=1, 那么无论x在什么位置, 第二个按钮都不能减少操作次数
如果r-l>1, 那么
x在l+2位置使用第二个按钮能够减少1次操作, 在l+3位置能减少2次… 在r位置能够减少r-(l+2)+1次操作
所以p[l+2] += 1, p[l+3] += 2 ... p[r] += r-(l+2)+1
对每一对l, r如此处理, 得到最后的p数组
设all为只使用第一个按钮所需要的操作总次数
那么`最少操作次数 = all - max{p[i]+p[i+m]}, 1<=i<=m

以上就是基本思路, 如果不加其他优化, 直接写的话, 复杂度 O(mn) O ( m n )
能优化的地方是p[l+2] += 1, p[l+3] += 2 ... p[r] += r-(l+2)+1这就是复杂度里m的来源, 可以用查分标记然后求两次前缀和, 将它优化到 O(1) O ( 1 )


用差分标记将一个区间加上一个等差数列:

如果要将p[l]到p[r]依次加上1, 2, … r-l+1
我们可以这样: p[i] += 1, p[r+1] -= r-l+1 + 1, p[r+2] += r-l+1, 每次只更新这三个值, 最后再从头到尾p[i] += p[i-1]
具体的一组例子, 比如l=2, r=5, 要将区间[l, r]中的元素依次加上{1, 2, 3, 4}:

更新操作复杂度1234567
更新三个值 O(1) O ( 1 ) 01000-54
p[i]+=p[i-1] O(n) O ( n ) 01111-40
p[i]+=p[i-1] O(n) O ( n ) 0123400

所以最后总复杂度 O(n+m) O ( n + m )

参考自: 来源

代码

#include <bits/stdc++.h>

using namespace std;

const int MAXN = 3E5 + 100;
typedef long long ll;
int n, m, a[MAXN];
ll p[MAXN];

int main()
{
    ll all = 0;
    scanf("%d%d", &n, &m);
    for(int i=0; i<n; ++i) scanf("%d", a+i);
    for(int i=1; i<n; ++i)
    {
        int l = a[i-1], r = a[i];
        if(l>r) r += m;
        all += r-l;
        if(r-l>1)
        {
            p[l+2] += 1;
            p[r+1] -= (r-(l+2)+1) + 1;
            p[r+2] += (r-(l+2)+1);
        }
    }
    for(int i=1; i<=2*m; ++i) p[i] += p[i-1];
    for(int i=1; i<=2*m; ++i) p[i] += p[i-1];

    ll ans = -1;
    for(int i=1; i<=m; ++i) ans = max(ans, p[i]+p[i+m]);
    cout << all - ans << endl;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值