GDOI2018 Day1 T2 密码锁(贪心+差分)

Description

给定 a [ 1 ∼ n ] , a i ∈ [ 0 , m − 1 ] a[1 \sim n], a_i \in [0,m-1] a[1n],ai[0,m1],每次操作可以在模 m m m 意义下对任意一个区间 [ l , r ] [l,r] [l,r] 整体 + 1 +1 +1 − 1 -1 1,求最少几次操作可以使所有数字变成 0 0 0

Solution

a 1 ∼ n + 1 a_{1 \sim n + 1} a1n+1 进行在模意义下的差分,其中 a 0 = a n + 1 = 0 a_0 = a_{n+1} = 0 a0=an+1=0。有差分数组 b b b,在模意义下的区间加减转为选择一位 + 1 +1 +1,另一位 − 1 -1 1,要让 b b b 全为 0 0 0 m m m

b b b 从小到大排序。枚举找到一个分界点 m i d mid mid,用 m i d mid mid 左边的数 + 1 +1 +1,右边的数 − 1 -1 1 互相抵消。当 m i d mid mid 左边的数变成 0 0 0 的次数与 m i d mid mid 右边的数变成 m m m 的次数相等时

a n s = ∑ i = 1 m i d b i    = ∑ i = m i d + 1 n m − b i ans = \sum_{i = 1} ^ {mid} b_i \ \ = \sum_{i = mid + 1} ^ {n} m - b_i ans=i=1midbi  =i=mid+1nmbi

Code

#include 
using namespace std;
typedef long long ll;
const int N = 1e5 + 5, INF = 0x3f3f3f3f;
inline int read() {
	int x = 0, f = 0; char ch = 0;
	while (!isdigit(ch)) f |= ch == '-', ch = getchar();
	while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
	return f ? -x : x;
}
int n, m, a[N], b[N];  
ll suf, pre[N];  
int main() {
    n = read(), m = read();
    for (int i = 1; i <= n; i++) {
        a[i] = read();
        b[i] = (a[i] - a[i - 1] + m) % m;
    }
    b[n + 1] = (m - a[n]) % m;  
    sort(b + 1, b + n + 2);
    for (int i = 1; i <= n + 1; i++) pre[i] = pre[i - 1] + b[i];  
    for (int i = n + 1; i >= 1; i--) {
        suf += m - b[i];  
        if (suf == pre[i - 1]) {  
            printf("%lld\n", suf);
            break;
        }
    }
    return 0;
}

你可能感兴趣的:(GDOI2018 Day1 T2 密码锁(贪心+差分))