题目链接:https://arc077.contest.atcoder.jp/tasks/arc077_c
题意:两个按钮, 一个可以使计数器+1(计数器数字从1-m), 当前值为m时, 再+1就变成了1
另一个按钮储存了一个值x, 按一下就从任意值会变成x
n-1次操作, 由一个数组a[n]描述, 第i次操作: 将计数器从a[i]调到a[i+1]
将x设置为某个值, 使得所有操作需要按按钮的次数总和最小, 输出这个最小值
思考: 对于L=a[i] 和 R=a[i+1]
考虑 ①:R==L 不需要按 没啥用 ② : R-L==1 无论X是什么也没有用 ③ R-L>1 那么对于存在一个X在L-R区间
假如X=L+2可以减少按键一次 ~~~~~~~~~~~~~ X=R 可以减少R-L-1次,对于L>R的情况考虑R=R+m.这样就不需要考虑越界问题
因此维护X从1到m的值即可。ans记录如果都是按一下需要的最大总数。Ans记录最大的如果X在某位置的需要减少的次数
Ans=max(Ans,sum[i]+sum[i+m])(1<=i<=m) 因为有R=R+m 所以是i+m。这样的话暴力复杂度为n*m
所以利用前缀和优化成O(1)
我们可以这样: p[l+2] += 1, p[r+1] -= r-l, p[r+2] += r-l-1
, 每次只更新这三个值, 最后再从头到尾p[i] += p[i-1]
具体的一组例子, 比如l=2, r=5
更新操作 | 复杂度 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
更新三个值 | O(1)O(1) | 0 | 1 | 0 | 0 | 0 | -5 | 4 |
p[i]+=p[i-1] | O(n)O(n) | 0 | 1 | 1 | 1 | 1 | -4 | 0 |
p[i]+=p[i-1] | O(n)O(n) | 0 | 1 | 2 | 3 | 4 | 0 | 0 |
时间复杂度(n+m)
题解借鉴:https://blog.csdn.net/litmxs/article/details/74276105
#include<bits/stdc++.h>
using namespace std;
long long a[200005];
long long sum[200005];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
scanf("%lld",&a[i]);
long long ans=0;
for(int i=1;i<n;i++)
{
long long l=a[i-1];
long long r=a[i];
if(l>r)
r=r+m;
ans = ans + r- l;
if(r-l>1)
{
sum[l+2] = sum[l+2] +1;
sum[r+1] = sum[r+1] - (r-l) ;
sum[r+2] = sum[r+2] +(r-l-1);
}
}
for(int i=1;i<=2*m;i++)
sum[i] = sum[i] + sum[i-1];
for(int i=1;i<=2*m;i++)
sum[i]= sum[i] + sum[i-1];
long long Ans=0;
for(int i=1;i<=m;i++)
Ans= max(Ans, sum[i] + sum[i+m]);
printf("%lld\n",ans-Ans);
return 0;
}