Codeforces Round #636, D. Constant Palindrome Sum, 区间+前缀和

题意,一个数组 a a a,包含 n n n个整数,所有 a a a i _i i都不超过一个整数 k k k,你可以做一些替换数组中元素的操作:将 a a a i _i i替换为 [ 1 ; k ] [1;k] [1;k]范围中的数,最终使得整个数组满足如下条件:

  • 在替换完成后,所有的 a a a i _i i是正整数且不超过 k k k;
  • i i i 1 1 1~ n 2 \frac{n}{2} 2n满足条件: a a a i _i i+ a a a n − i + 1 _{n-i+1} ni+1= x x x,对所有的 n 2 \frac{n}{2} 2n来说, x x x都相同。

需要求的是最少的替换次数使数组满足条件。

用例:

input:
4 3
1 2 2 1
output:
1

// 解释:将a[0]替换为3,即可满足条件

题解:
首先要分析三种情况:假设已知 x x x可以使得替换次数最少。那么:

  1. a a a i _i i+ a a a n − i − 1 _{n-i-1} ni1=x, 不用替换
  2. m i n ( min( min( a a a, a a a n − i − 1 _{n-i-1} ni1 ) ) )+ 1 1 1 < = x <=x <=x && m a x ( max( max( a a a, a a a n − i − 1 _{n-i-1} ni1 ) ) )+ k k k > = x >= x >=x ,那么只需要替换一个元素就可以使得二者的和等于 x x x,那么这也就是一个区间 [ m i n ( a , a [min(a,a [min(a,a n − i − 1 _{n-i-1} ni1 ) + 1 , m a x ( a , a )+1,max(a,a )+1,max(a,a n − i − 1 _{n-i-1} ni1 ) + k ] )+k] )+k],只要 x x x的值在这个区间内,那么就只用替换一个元素。
  3. 上述两个条件都不满足,那么必须得同时替换 a a a i _i i a a a n − i − 1 _{n-i-1} ni1才能使得等于 x x x,也就是得替换两个。

所以,如果把左区间的端点 l l l设为+ 1 1 1,右区间的端点 r + 1 r+1 r+1设为- 1 1 1,那么再对所有端点的值求一个前缀和 p r e pre pre,就可以得到 p r e pre pre x _x x有多少对组合满足只替换一个就可以满足和为 x x x的条件,同时需要减去 c n t cnt cnt x _x x的值, c n t cnt cnt x _x x是和为 x x x的已有的个数,所以最少替换一个的值就是 p r e pre pre x _x x- c n t cnt cnt x _x x
那么还有得替换两个的怎么求?因为 p r e pre pre x _x x为替换一个能满足条件的个数,并且不用替换的也被包含在了其中,所以得替换两个的个数就是 ( ( ( n 2 \frac{n}{2} 2n- p r e pre pre x _x x ) ) ) ∗ 2 *2 2

对每一个可能的 x x x进行遍历,求出其最小的 p r e pre pre x _x x- c n t cnt cnt x _x x+ ( ( ( n 2 \frac{n}{2} 2n- p r e pre pre x _x x ) ) ) ∗ 2 *2 2

#include <bits/stdc++.h>
using namespace std;

int main() {
	int t;
	cin >> t;
	while (t--) {
		int n, k;
		cin >> n >> k;
		vector<int> a(n);
		for (int i = 0; i < n; i++) {
			cin >> a[i];
		}
		
		vector<int> cnt(2*k+1), pre(2*k+1);
		for (int i = 0; i < n/2; i++) {
			cnt[a[i] + a[n-i-1]]++;
		}
		
		for (int i = 0; i < n/2; i++) {
			int Min = min(a[i], a[n-i-1]) + 1;
			int Max = max(a[i], a[n-i-1]) + k;
			pre[Min]++;
			pre[Max+1]--;
		}
		
		for (int i = 1; i <= 2*k; i++) {
			pre[i] = pre[i-1] + pre[i];
		}
		
		int res = n;
		for (int i = 1; i <= 2*k; i++) {
			res = min(res, (pre[i] - cnt[i]) + (n/2 - pre[i])*2);
		}
		cout << res << endl;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值