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/2a[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~k1 k之间
- 分析:
- 要想得到最小的变化次数,我就要找到一个合适的x xx,使a中的元素尽可能的少改变,最好不改变,,,但是怎找到这个合适的x呢??
- 我们的解决分案也很简答粗暴,那就是暴力枚举所有可能的x,因为1 = < a r [ i ] < = k , 1=<ar[i]<=k~~,~1=<ar[i]<=k , 所以2 < = x < = 2 k 2<=x<=2k2<=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]-1pref[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;
}
版权声明:本文为qq_34261446原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。