Codeforces Round #636 (Div. 3)

Codeforces Round #636 (Div. 3).D题

/*
题目D:
给你含有n个元素的一个数列以及一个整数k,每个元素大小不能超过k,且大于等于1;
现在让你用1-k的任意数字替换a的任意元素(可以替换无数次),让a[i]+a[n+1-i]=x恒成立,x可以是任意数字,但对于每个i来说要相等(即共有n/2组数)
求替换的最小次数
思路:
可以很显然看出最多n次就可以是这个数列变得满足条件,先求出每组数改变一次的上下界,
再利用线段树维护是的下界到上界的值都加一,代表x如果取在这个区间里的话,就最多只需要改变一次,即省下了一次改变
再用map储存一次都不改变的个数,最后枚举x求出最多能省多少次改变ans,输出n-ans即可
*/
#include <bits/stdc++.h>
#define INT 0x7fffffff
#define  cind(x) scanf("%d",&x)
#define cinc(x) scanf("%c",&x);while(ch==' '||ch=='\n')scanf("%c",&x);
#define kt  int t;scanf("%d",&t);while(t--)
#define mem0(x) memset(x,0,sizeof(x));
#define ll long long int
#define mem(x,y) memset(x,y,sizeof(x));
#define scanf scanf_s
const int maxn = 4e5 + 10;
int a[1000005];
using namespace std;
int s, n, m;
#define ls id<<1
#define rs id<<1|1
struct tree
{
	int l, r, sum;
	int tag;
}t[maxn * 4];
void pushdown(int id)
{
	t[ls].sum += (t[ls].r - t[ls].r + 1) * t[id].tag;
	t[rs].sum += (t[rs].r - t[rs].r + 1) * t[id].tag;
	t[ls].tag += t[id].tag;
	t[rs].tag += t[id].tag;
	t[id].tag = 0;
}
void build(int id, int l, int r)
{
	t[id].l = l, t[id].r = r, t[id].tag = 0;
	if (l == r)
	{
		t[id].sum = 0;
		return;
	}
	int mid = (l + r) / 2;
	build(ls, l, mid);
	build(rs, mid + 1, r);
	t[id].sum = t[ls].sum + t[rs].sum;
}
void change(int id, int l, int r, int p)
{
	if (l <= t[id].l && r >= t[id].r)
	{
		t[id].sum += (t[id].r - t[id].l + 1) * p;
		t[id].tag += p;
		return;
	}
	if (t[id].tag) pushdown(id);
	int mid = (t[id].l + t[id].r) / 2;
	if (l <= mid) change(ls, l, r, p);
	if (r > mid) change(rs, l, r, p);
	t[id].sum = t[ls].sum + t[rs].sum;
}
int query(int id, int l, int r)
{
	int res = 0;
	if (l <= t[id].l && r >= t[id].r)
		return t[id].sum;
	if (t[id].tag) pushdown(id);
	int mid = (t[id].l + t[id].r) / 2;
	if (l <= mid) res += query(ls, l, r);
	if (r > mid) res += query(rs, l, r);
	return res;
}
int main()
{
	int i, j,k;
	int t;
	cin >> t;
	while (t--)
	{
		cin >> n >> k;
		for (i = 1; i <= n; i++)
		{
			cin >> a[i];
		}
		build(1, 1, 2*k);//建立线段树
		map<int, int>mp;
		for (i = 1; i <= n/2; i++)
		{
			int st = min(a[i], a[n + 1 - i]) + 1;//计算第i组只更改一次的下界
			int ed = max(a[i], a[n + 1 - i]) + k;//计算第i组只更改一次的上界
			change(1, st, ed, 1);//将第i组的下界到上界加一,因为正常来说一组有两个数可以更改两次,如果x的取值在这个范围内则可以减少一次改变,即只需改变一次就可以等于x,用了线段树的区间修改
			mp[a[i] + a[n + 1 - i]]++;//将这组原本的和记录下来
		}
		int ans = 0;//表示减少改变的最大次数
		for (i = 1; i <= k * 2; i++)//从1到k*2枚举x的取值
		{
			ans = max(ans, query(1, i, i) + mp[i]);//ans表示减少的最大次数,query(1,i,i)表示求出x=i时的减少次数,即单点查询,mp[i]表示原本和就是i的组数,总减少数就是query(1, i, i) + mp[i]
		}
		cout << n - ans << endl;//显而易见最多更改n次就可以使原本的n/2组数相等,因此用n减去最大的减少改变次数ans即可
		
	}

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值