AtCoder Regular Contest 077 E(upc 6576) guruguru

22 篇文章 0 订阅
9 篇文章 0 订阅

题目链接: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

更新操作复杂度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

时间复杂度(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;
} 

 

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值