hihocoder1384 Genius ACM(倍增)

题目

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

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


题解

倍增+排序+暴力

题意中“每对数的差的平方”之和最大是首要解决任务,根据数学猜想,很容易想到把最大和最小组合,把次大和次小组合,以此类推。这里就涉及到了顺序问题。
朴素做法很容易想到,从1往n推,一旦超过k,就切换到下一个区间。让每个区间都尽量大,最终的段数一定最小。
这样的枚举实在太慢了,要优化有两条路可以走,一是二分,二是倍增,我选择了倍增做法。
其实倍增和二分待解决的问题是一样的,都是要做一个判断l~r的校验值是否在k之内。
第一步,最麻烦的,使区间内的元素有序。最简单的方法就是每加一个数进来就排一次序,可是TLE了。优化一下,把新增区间用归并排序并到原区间中。但是不要着急赋给它,因为有可能会失败。要等到符合题意后,再赋值。
第二步,求出该区间最大的“每对数的差的平方”之和。

第三步,判断要增大r,还是坎半p。


代码

#include
#include
#include
using namespace std;
typedef long long ll;
const int maxn=5e5+10;


inline ll read()
{
	ll re=0;char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') re=re*10+(ch^48),ch=getchar();
	return re;
}


int n,m;ll k;
int a[maxn],b[maxn],c[maxn];
//a存贮原始数据 b存当前排好序的序列 c存最新排好序的序列(注意:只在特定范围内有序) 


void merge(int l,int r,int mid)//合并b l~mid-1和mid~r 
{
	int i=l,j=mid;
	for(int k=l;k<=r;k++)
		if( j>r || (i>=1;//倍减 
			}
			l=r+1;
		}
		printf("%lld\n",ans);
	}
	return 0;
}


你可能感兴趣的:(刷题之路,归并排序,排序,倍增,《算法竞赛进阶指南》刷书之旅)