guruguru
时间限制: 1 Sec 内存限制: 128 MB
提交: 44 解决: 19
[提交] [状态] [讨论版] [命题人:admin]
题目描述
Snuke is buying a lamp. The light of the lamp can be adjusted to m levels of brightness, represented by integers from 1 through m, by the two buttons on the remote control.
The first button is a "forward" button. When this button is pressed, the brightness level is increased by 1, except when the brightness level is m, in which case the brightness level becomes 1.
The second button is a "favorite" button. When this button is pressed, the brightness level becomes the favorite brightness level x, which is set when the lamp is purchased.
Snuke is thinking of setting the favorite brightness level x so that he can efficiently adjust the brightness. He is planning to change the brightness n−1 times. In the i-th change, the brightness level is changed from ai to ai+1. The initial brightness level is a1. Find the number of times Snuke needs to press the buttons when x is set to minimize this number.
Constraints
2≤n,m≤105
1≤ai≤m
ai≠ai+1
n, m and ai are integers.
输入
Input is given from Standard Input in the following format:
n m
a1 a2 … an
输出
Print the minimum number of times Snuke needs to press the buttons.
样例输入
4 6
1 5 1 4
样例输出
5
题意
两个按钮, 一个可以使计数器+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]
设置一个数组sum, sum[i] := 如果x在i位置, 对于所有操作, 使用第二个按钮能够减少的操作次数(相对于只使用第一个按钮)
(从a[i]到a[i+1]档,可以按a[i+1]-a[i]次,假如x设置在其中间位置,只需按a[i+1]-x+1次即可,那么可以将每个a[i]和a[i+1]看成一条线段,只要x设置在线段上,这次操作就有相应的次数减少,减少的次数为x-a[i]-1次,x在线段外即对当前操作无影响)
考虑 ①:R==L 不需要按 没啥用 ② : R-L==1 无论X是什么也没有用 ③ R-L>1 那么对于存在一个X在L-R区间
假如X=L+2可以减少按键一次
x在l+2
位置使用第二个按钮能够减少1次操作, 在l+3
位置能减少2次… 在r位置能够减少r-(l+2)+1
次操作
所以sum[l+2] += 1, sum[l+3] += 2 ... sum[r] += r-(l+2)+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)
如果要将sum[l]到sum[r]依次加上1, 2, … r-l+1
我们可以这样: sum[i] += 1, sum[r+1] -= r-l+1 + 1, sum[r+2] += r-l+1
, 每次只更新这三个值, 最后再从头到尾sum[i] += p[i-1]
具体的一组例子, 比如l=2, r=5
差分数组(区间内每次加一,恰好为前缀和)
理解的话可以从下面往上面理解然后写代码的话从上往下写
更新操作 | 复杂度 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
更新三个值 | O(1) | 0 | 1 | 0 | 0 | 0 | -5 | 4 |
p[i]+=p[i-1] | O(n) | 0 | 1 | 1 | 1 | 1 | -4 | 0 |
p[i]+=p[i-1] | O(n) | 0 | 1 | 2 | 3 | 4 | 0 | 0 |
所以最后总复杂度O(n+m)
来自大佬的思路,打死我也想不到
#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;
}