Codeforces1343D Constant Palindrome Sum(差分)

题目链接OvO

题目大意

  给一个长度为偶数且每个元素不大于\(k\)的正整数序列,你可以让任意一个元素修改为一个不大于\(k\)的正整数,求使每对\(a_i\)\(a_{n-i+1}(i\leq \frac{n}{2})\)的和都相等的最小修改次数。

分析

  对于每对数来说,它们可修改得到数字都有一个上界和一个下界,上界就是让最小的数变成\(k\),下界就是让最大的数变成\(1\)。假设答案中的序列任意两对数之和都为\(x\),那么对于问题中的每每对数,如果小于或者大于它们的上下界的话,那么就得修改两次;如果在上下界之内的话就得修改一次;特别的,如果等于两数之和,则不用修改。

具体实现

  性质很好分析但是怎么利用它呢?之前想过二分,但是没成到现在也不知道怎么改,看到大家都是用差分数组做的,这样做出来时间复杂度也比二分更低。大致思路就是用差分数组计算每个数对于各个范围之内的数的贡献。

如果位于上下界之外

  令下界\(sub[2] += 2, sub[minn] -= 2\)和上界\(sub[maxx+1] += 2, sub[2*k+1]-=2\)(也就是对于某对数\(x\)\([2,minn-1]\cup [maxx+1,2\times k]\)之内的值都要修改两次)

如果位于上下界之内

  令\(sub[minn] += 1,sub[maxx+1] -= 1\)对应\(x\)取在上下界之内的情况

如果位于上下界之内刚好等于\(x\)

  令\(sub[arr[i]+arr[n-i+1]] -= 1, sub[arr[i]+arr[n-i+1]+1] += 1\)也就是说当\(x\)取这个值的时候贡献为\(0\)

代码

const int maxn = 2e5+10;
int arr[maxn], sub[maxn<<1];
int main(void) {
    int t;
    scanf("%d", &t);
    while(t--) {
        int n, k;
        scanf("%d%d", &n, &k);
        memset(sub, 0, sizeof(sub[0])*(2*k+5));
        for (int i = 1; i<=n; ++i) scanf("%d", &arr[i]);
        for (int i = 1; i<=n/2; ++i) {
            int maxx = max(arr[i], arr[n-i+1])+k;
            int minn = min(arr[i], arr[n-i+1])+1;
            sub[2] += 2; sub[minn] -= 2;
            sub[maxx+1] += 2; //sub[2*k+1]-=2就不写了,用不上
            sub[minn] += 1; sub[maxx+1] -= 1;
            sub[arr[i]+arr[n-i+1]] -= 1; sub[arr[i]+arr[n-i+1]+1] += 1;
        }
        int ans = INF;
        for (int i = 2; i<=2*k; ++i) {
            sub[i] += sub[i-1];
            ans = min(ans, sub[i]);
        }
        printf("%d\n", ans);
    }
    return 0;
}
posted @ 2020-04-23 11:32  shuitiangong  阅读(170)  评论(0编辑  收藏  举报