D. Constant Palindrome Sum
思路
- 题意:给我们一个长度为n的数组a,n恒为偶数,之后有给了一个k并且给了他的n个元素的值,先在我们需要对这个序列做变化,使
a
[
i
]
+
a
[
n
−
i
+
1
]
=
=
x
,
1
<
=
i
<
=
n
/
2
a[i]+a[n-i+1]==x,1<=i<=n/2
a[i]+a[n−i+1]==x,1<=i<=n/2,x为一个常数,问我们最少需要变化多少个数,有一个常数x使上式成立,对于每一个变换我们可以把序列中的任意一个元素
a
[
i
]
a[i]
a[i]变为1~k之间的任意一个数,求最小需要变化的元素的数量
注意:无论 a r [ i ] ar[i] ar[i]变化与否它的范围都是在 1 k 1~k 1 k之间
- 分析:
- 要想得到最小的变化次数,我就要找到一个合适的 x x x,使a中的元素尽可能的少改变,最好不改变,,,但是怎找到这个合适的x呢??
- 我们的解决分案也很简答粗暴,那就是暴力枚举所有可能的x,因为 1 = < a r [ i ] < = k , 1=<ar[i]<=k~~,~ 1=<ar[i]<=k , 所以 2 < = x < = 2 k 2<=x<=2k 2<=x<=2k
- 我们面的情况有三种
- 第一个种一个都不用改,那么我们直接统计 a [ i ] + a [ n − i + 1 a[i]+a[n-i+1~ a[i]+a[n−i+1 的和出现的次数,这个“和”是有可能作为后选的x的
- 第二种的情况我可以改变
a
[
i
]
、
a
r
[
n
−
i
+
1
]
a[i]、ar[n-i+1]
a[i]、ar[n−i+1]这两个数中的一个数,那么改完之后
a
[
i
]
+
a
r
[
n
−
i
+
x
]
a[i]+ar[n-i+x]
a[i]+ar[n−i+x] 所能表示的范围是
[
m
i
n
(
a
[
i
]
,
a
[
n
−
i
+
1
)
+
1
,
m
a
x
(
a
[
i
]
,
a
[
n
−
i
+
1
]
+
k
]
[min(a[i],a[n-i+1)+1,~max(a[i],a[n-i+1]+k]
[min(a[i],a[n−i+1)+1, max(a[i],a[n−i+1]+k]在这个表示的区间范围内都是可以表示的数,都可以作为待选的x我们也要统计这个区件内的数当i的取值不同的的出现的次数,统计这个事情就要
差分数组
来维护了,怎么维护,考虑我们枚举的区间x的为 [ 2 , 2 k ] [2,2k] [2,2k],我们假设差分数组为 p r e f [ ] pref[] pref[],由于我们改变 a [ i ] 、 a r [ n − i + 1 ] a[i]、ar[n-i+1] a[i]、ar[n−i+1]这两个数中的一个数,那么改完之后 a [ i ] + a r [ n − i + x ] a[i]+ar[n-i+x] a[i]+ar[n−i+x] 所能表示的范围是 [ m i n ( a [ i ] , a [ n − i + 1 ) + 1 , m a x ( a [ i ] , a [ n − i + 1 ] + k ] [min(a[i],a[n-i+1)+1,~max(a[i],a[n-i+1]+k] [min(a[i],a[n−i+1)+1, max(a[i],a[n−i+1]+k]在这个表示的区间范内的数进行区间+1操作
,而转化为差分上的操作就是: p r e f [ m i n ( a [ i ] , a [ n − i + 1 ) + 1 ] + 1 , p r e f [ m a x ( a [ i ] , a [ n − i + 1 ] ) + k ] − 1 pref[min(a[i],a[n-i+1)+1]+1,~~~~~pref[max(a[i],a[n-i+1])+k]-1 pref[min(a[i],a[n−i+1)+1]+1, pref[max(a[i],a[n−i+1])+k]−1 - 第三种情况是改变两个数,改变两个数那么 a [ i ] + a [ n − i + 1 ] a[i]+a[n-i+1] a[i]+a[n−i+1]可以表示 [ 2 , 2 k ] [2,2k] [2,2k]之间的任何数,所以留到最后在考虑就行了
- 考虑完这三种情况之后就是一一枚举可能的x了
代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
void fre() { freopen("A.txt","r",stdin); freopen("Ans.txt","w",stdout); }
#define INF 0x3f3f3f3f
int main()
{
/* fre(); */
int t;
scanf("%d", &t);
while(t --)
{
int n, k;
scanf("%d %d", &n, &k);
vector<int> ar(n + 1);
for(int i = 1; i <= n; i ++)
scanf("%d", &ar[i]);
vector<int> cnt(2 * k + 1);
//第一种一个都不需要更改
for(int i = 1; i <= n/2; i ++)
cnt[ar[i] + ar[n - i + 1]] ++;
//第二种只需要改一个
vector<int> pref(2*k + 2);
for(int i = 1; i <= n/2; i ++)
{
int l = min(ar[i], ar[n - i + 1]) + 1; //[l, r] 更改ar[i] 或者ar[n - i + 1] 其中一个数所能得到的最多范围
int r = max(ar[i], ar[n - i + 1]) + k;
//先用差分维护需要加值的区间
pref[l] ++;
pref[r + 1] --;
}
//差分前缀和,用来表示区间中的增加值之后的数
for(int i = 1; i <= 2*k; i ++)
pref[i] += pref[i - 1];
//对剩下的第三种情况,结合前两种进行考虑
int ans = INF;
for(int i = 2; i <= 2*k; i ++)
ans = min(ans, (pref[i] - cnt[i]) + 2*(n/2 - pref[i]));
printf("%d\n", ans);
}
return 0;
}