Codeforces1343D Constant Palindrome Sum(差分)
题目大意
给一个长度为偶数且每个元素不大于\(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;
}