cf 1443 D. Extreme Subtraction

传送门
感觉这题好棒啊,我就是想不到,如果没有看题解,我可能想个10年也想不出来。
就是给出n个数字,你需要进行操作,任意替换一个数字,变成([1:k]),然后使得最后满足(a[i] + a[n - i + 1])都相等。
求最小操作次数。

看了一眼以为是三分的。以为看见题目里的x,想着枚举x。
但是差分可以解决,就是对于每个(i in[1,frac{n}{2}])(a[i] + a[n - i + 1])的贡献值为0,表示可以到达,然后更改1次的区间的贡献值+1,更改2次的区间贡献值+2,注意的是三个区间不能够有并的关系就行了。然后分类讨论进行差分。最后求下前缀和,取最小值

int a[N], b[N];
int n, k;
void change(int l, int r, int pos, int c){
    if(r == l) {
        if(l != pos) b[l] += c, b[l + 1] -= c;
        return;
    }
    if(l <= pos && pos <= r) {
        if(pos == l) b[l + 1] += c, b[r + 1] -= c;
        else if(pos == r) b[l] += c, b[r] -= c;
        else {
            if(r - l + 1 >= 3) {
                b[l] += c, b[pos] -= c;
                b[pos + 1] += c, b[r + 1] -= c;
            }
        }
    }else b[l] += c, b[r + 1] -= c;
}
void solve(int kase){
    _(n), _(k);
    for(int i = 1; i <= n; i++) _(a[i]);
    for(int i = 0; i <= 2 * k + 2; i++) b[i] = 0;
    for(int i = 1; i <= n / 2; i++) {
        int mi = min(a[i], a[n - i + 1]);
        int mx = max(a[i], a[n - i + 1]);
        int lone = 1 + mi, rone = mx + k;
        int ltwo = 2, rtwo = 2 * k;
        int now = a[i] + a[n - i + 1];
        change(lone, rone, now, 1);
        if(lone != 2) {
            change(ltwo, lone - 1, now, 2);
        }
        if(rone != 2 * k) {
            change(rone + 1, rtwo, now, 2);
        }
    }
    int ans = INF, now = 0;
    for(int i = 1; i <= 2 * k; i++) b[i] += b[i - 1];
    for(int i = 2; i <= 2 * k; i++)
        ans = min(ans, b[i]);
    printf("%d
", ans);
}
原文地址:https://www.cnblogs.com/Emcikem/p/14254902.html