CH0601 Genius ACM

1 篇文章 0 订阅
1 篇文章 0 订阅

题意

给定一个整数 M M M,对于任意一个整数集合 S S S,定义“校验值”如下:

从集合 S S S中取出 M M M对数(即 2 ∗ M 2∗M 2M个数,不能重复使用集合中的数,如果 S S S中的整 数不够 M M M对,则取到不能取为止),使得“每对数的差的平方”之和最大,这个最大值 就称为集合 S S S的“校验值”。

现在给定一个长度为 N N N的数列 A A A以及一个整数 T T T。我们要把 A A A分成若干段,使得 每一段的“校验值”都不超过 T T T。求最少需要分成几段。

思路

显然集合 S S S的“校验值”为 S S S中最大值和最小值匹配,次大值和次小值匹配 ⋯ \cdots 以此类推。
朴素做法是暴力枚举分界点,使得在每段的“校验值”不超过 T T T的条件下尽可能长,直接排序计算扩展段的“校验值”,时间复杂度 O ( n 2 l o g n ) O(n^2logn) O(n2logn)
考虑优化上述做法,用倍增扩展分界点,时间复杂度可降为 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n),若扩展的时候不是重新排序,先对新扩展段进行排序,再用类似归并排序的方法合并原段和新扩展段,时间复杂度可降为 O ( n l o g n ) O(nlogn) O(nlogn)

代码

#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cstdio>
#include <cmath>
#include <iostream>
using namespace std;

#define fo(i, x, y) for (int i = x; i <= y; ++i)
#define fd(i, x, y) for (int i = x; i >= y; --i)

typedef long long ll;

const int maxn = 5e5 + 5;

int n, m;
int a[maxn], b[maxn];
ll k;

int getint()
{
	char ch;
	int res = 0, p;
	while (!isdigit(ch = getchar()) && (ch ^ '-'));
	p = ch == '-'? ch = getchar(), -1 : 1;
	while (isdigit(ch))
		res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
	return res * p;
}

ll getval(int l, int r)
{
	//fo(i, l, r) b[i] = a[i];
	//sort(b + l, b + r + 1);
	ll res = 0;
	for (int pl = l, pr = r, cnt = 0; pl < pr && cnt < m; pl++, pr--, cnt++)
		res += (ll)(b[pr] - b[pl]) * (b[pr] - b[pl]);
	return res;
}

int main()
{
	//freopen("input", "r", stdin);
	int kase;
	kase = getint();
	while (kase--)
	{
		n = getint(); m = getint(); scanf("%lld", &k);
		fo(i, 1, n) a[i] = getint();
		int ans = 0;
		for (int len = 1, l = 1, r = 1; r <= n;)
		{
			len = 1;
			while (len)
			{
				if (r + len <= n)
				{
					fo(i, r + 1, r + len) b[i] = a[i];
					sort(b + r + 1, b + r + len + 1);
					merge(a + l, a + r + 1, b + r + 1, b + r + len + 1, b + l);
				}
				if (r + len <= n && getval(l, r + len) <= k)
				{
					r += len, len <<= 1;
					fo(i, l, r) a[i] = b[i];
				}
					else len >>= 1;
			}
			l = r = r + 1;
			ans++;
		}
		printf("%d\n", ans);
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值