Increasing by Modulo

Toad Zitz has an array of integers, each integer is between 0 and m−1 inclusive. The integers are a1,a2,…,an.
In one operation Zitz can choose an integer k and k indices i1,i2,…,ik such that 1≤i1<i2<…<ik≤n. He should then change aij to ((aij+1)modm) for each chosen integer ij. The integer m is fixed for all operations and indices.
Here xmody denotes the remainder of the division of x by y.
Zitz wants to make his array non-decreasing with the minimum number of such operations. Find this minimum number of operations.

Input
The first line contains two integers n and m (1≤n,m≤300000) — the number of integers in the array and the parameter m.

The next line contains n space-separated integers a1,a2,…,an (0≤ai<m) — the given array.

Output
Output one integer: the minimum number of described operations Zitz needs to make his array non-decreasing. If no operations required, print 0.

It is easy to see that with enough operations Zitz can always make his array non-decreasing.

Sample:
Input:

5 3
0 0 0 1 2

OutPut:

0

Input:

5 7
0 6 1 3 2

OutPut:

1

Note
In the first example, the array is already non-decreasing, so the answer is 0.

In the second example, you can choose k=2, i1=2, i2=5, the array becomes [0,0,1,3,3]. It is non-decreasing, so the answer is 1.

题目大意:
给定一个序列,每次可以操作数组中任意多个数字,每次操作可以使当前位置的数字 arr[i] 变成 (arr[i]+1)%m,求至少多少次操作可以使序列变成一个非递减的序列(不严格的单调递增)。

思路
对每个数字操作 m 次和没操作一样,所以每个数最多操作 m-1 次。可以二分每个数的操作次数,如果当前操作次数为 mid ,如果对该数字操作 0 次到 mid 次没有任何一次符合条件,说明该数字操作 mid 次不能满足条件,二分的区间应该将小的一部分舍去,以此类推,如果满足条件,说明在当前的区间内,某一个数满足的条件,仅有一个数字满足条件是不行的,所以还要枚举数组中的每一个数字,如果全部满足条件,就能将二分区间缩小,舍去大的部分。

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
#ifdef ONPC
mt19937 rnd(228);
#else
mt19937 rnd(chrono::high_resolution_clock::now().time_since_epoch().count());
#endif

int main() {
#ifdef ONPC
	freopen("a.in", "r", stdin);
#endif
	ios::sync_with_stdio(0);
	cin.tie(0);
	int n, m;
	cin >> n >> m;
	vector <int> a(n);
	for (int &x : a) {
		cin >> x;
	}
	int l = -1, r = m;
	while (l < r - 1) {
		int mid = (l + r) / 2;
		int prev = 0;
		//prev 代表的前一个位置的数字 
		bool bad = false;
		//bad 判断能否满足条件 
		for (int i = 0; i < n; i++) {
			int lf = a[i], rf = a[i] + mid;
			if ((lf <= prev && prev <= rf) || (lf <= prev + m && prev + m <= rf)) {
				continue;
				//如果通过mid次操作能让前面的点小于等于后面的点,说明可行 
			}
			if (lf < prev) {
				//从上面的循环出来,不满足上述条件并且 lf < prev 说明 prev > rf
				//即 mid 次操作不能让前面的点小于等于后面的点,此操作次数不可行 
				// mid 次不行,比 mid 小更不行 
				bad = true;
				break;
			}
			else {
				//如果 prev <= lf ,也就是说出现了一个比 prev 更小的值
				//这时为了保证操作尽可能少,可以直接不操作,让 prev = lf 
				prev = lf;
			}
		}
		if (bad) {
			l = mid;
		}
		else {
			r = mid;
		}
	}
	cout << r << '\n';
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值