CodeForces - 1168A Increasing by Modulo(二分、贪心)

Increasing by Modulo

题目大意:

选一个下标的长度为\(k\)的子序列\(b_1,b_2,...,b_k\),使得\(a_{b_i} = (a_{b_i} + 1) \% m\),求使得序列满足单调非降的最小操作次数。

思路:

设\(f(x)\)为能否经过\(x\)次操作数使得序列满足条件,不难看出这是一个单调函数,考虑利用二分求出最小操作次数,假设当前的二分的操作次数为\(x\)。

对于check的编写,我们首先考虑到,题目中的操作对于序列第\(i\)位数字\(a[i]\)的变换是相对独立的,也就是说,我们只需check序列经过\(x\)次变换后的结果序列,即判断每个数字的变换次数是否符合当前二分的\(x\),并且判断能否让结果序列满足单调非降。

对于确定二分操作次数的上界,我们考虑到,经过\(m\)次变换之后,我们一定可以将所有数字都变为\(0\),由此确定出二分上界。

接下来,我们贪心的进行变换。

假设当前check到第\(i\)位数字\(a[i]\):

  1. 如果\(a[i] = a[i - 1]\),不需要进行变换。
  2. 如果\(a[i] < a[i - 1]\),要想满足单调不降就需要对\(a[i]\)进行操作,如果\(a[i]\)与\(a[i - 1]\)的距离大于了二分的次数\(x\),直接return,否则,使\(a[i] := a[i - 1]\),让序列升的不那么多(这里是不让他上升)以贪心的期望后面会更加容易的满足单调非降。
  3. 如果\(a[i] > a[i - 1]\),那么如果能变为\(a[i - 1]\)就变为\(a[i - 1]\),否则不改变。

举个例子

a[i - 1]   a[i]
    5        3
    4        7

主要是a[i] > a[i - 1]时:

  a[i - 1]
|----------|
         a[i]                    m
|-----------****************|--------------|

因为操作是加操作,所以除非满足\(m - (a[i] - a[i - 1]) \leq x\),使得\(a[i]\)变换为\(a[i - 1]\),否则,\(a[i]\)只能落在上图‘*’号的左边或者右边,落在左边则不满足单调不降的条件,落在右边则还不如不变换。

Code:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const double eps = 1e-6;
const int N = 300010;
//const int N = 10;
const int INF = 0x3f3f3f3f;
const int mod = 1000000007; //998244353
const int dir[8][2] = {0, 1, 0, -1, 1, 0, -1, 0,/* dir4 */ -1, -1, -1, 1, 1, -1, 1, 1};
ll gcd(ll a, ll b) { return b == 0 ? a : gcd(b, a % b); }
ll powmod(ll a, ll b) { ll res = 1; a %= mod; assert(b >= 0); for (; b; b >>= 1) { if (b & 1) res = res * a % mod; a = a * a % mod; } return res; }
template<class T> bool ckmin(T& a, const T& b) { return b < a ? a = b, 1 : 0; }
template<class T> bool ckmax(T& a, const T& b) { return a < b ? a = b, 1 : 0; }

ll n, m, a[N], oa[N];

bool ck(ll x) {
    for (int i = 1; i <= n; i++)
        a[i] = oa[i];
    for (ll i = 1; i <= n; i++) {
        if (a[i] == a[i - 1]) {
            continue;
        } else if (a[i] < a[i - 1]) {
            if (a[i - 1] - a[i] > x) {
                return false;
            } else {
                a[i] = a[i - 1];
            }
        } else {
            if (m - (a[i] - a[i - 1]) <= x) {
                a[i] = a[i - 1];
            }
        }
    }
    return true;
}

int main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> oa[i];
    }
    ll lf = 0, rt = m/* m次操作可以让所有数都变为0 */, mid;
    while (lf < rt) {
        mid = (lf + rt) >> 1;
        if (ck(mid)) {
            rt = mid;
        } else {
            lf = mid + 1;
        }
    }
    cout << lf << "\n";
    return 0;
}
上一篇:hash实现


下一篇:Codeforces Round #716 (Div. 2) C. Product 1 Modulo N