【二分答案】【中位数】codeforces 394 bun

bun
Description
因为体育老师很喜欢等差数列,所以他要求学生们站队必须按身高站成等差数列。
但是有些班级的学生无论如何也无法排成等差数列,于是体育老师从食堂买来了两种神
奇的面包。吃一个第一种面包可以使身高增 1,吃一个第二种面包可以使身高减 1。
你的任务是,对于某个班级,帮助老师安排哪些同学食用多少面包。考虑到学生的身体
健康,体育老师希望吃面包最多的学生吃的面包数量尽量少。
Input Format
第一行一个正整数 n,表示学生的数目。
第二行是长度为 n 的整数序列 a, a[i]表示第 i 个学生的身高。由于体育老师使用的奇怪
的身高单位,学生身高可能是负数。可以打乱顺序重新排列。
Output Format
第一行一个整数,表示吃面包最多的学生吃的面包数量。
第二行两个整数,分别表示等差数列的首项和公差,公差不能为负。
如果有多解,输出公差最小的方案。如果还有多解,输出首项最小的方案。
Sample Input (1)
5
-3 -4 -2 -3 3
Sample Output (1)
2
-3 1
Sample Input (2)
5
2 -3 -1 -4 3
第 8 页 共 8 页
Sample Output (2)
1
-4 2
Hint
对于 30%的数据,n<=100,a[i]的绝对值<=1000。
对于 60%的数据,n<=1000,a[i]的绝对值<=100000。
对于 100%的数据,n<=100000,a[i]的绝对值<=1000000。

<法一>记f1为当前需要吃的增高面包的最大的量,f2为当前需要吃的减低面包最大的量。

考虑暴力的情况,枚举公差d,我们可以通过计算f1、f2,再取中位数的方式得到当前的最优首项。

我们把每个d对应的f1、f2打出表来,就可以发现,f1单调递减,f2单调递增,它们两个相交的位置就是答案,二分即可(怕写错,写了分块答案)。

<法二>orz faebdc,我们在枚举d的时候,易发现,d最大不会超过4*max(a[i])/n,否则还不如把a全变0更优,因此暴力就好啦~

#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define N 100001
int n,a[N],ans=2147483647,b[N],f1,f2,D,X0;
int main()
{
	scanf("%d",&n);
	if(n<=1000){
	for(int i=1;i<=n;++i) scanf("%d",&a[i]);
	sort(a+1,a+n+1);
	int lim=30000000/n;
	f1=f2=0;
	for(int i=1;i<=n;++i)
	  {
	  	b[i]=a[1]-a[i];
	  	if(b[i]>0) f2=max(f2,b[i]);
		else f1=max(f1,-b[i]);
	  }
	if(ans>((f1+f2+1)>>1))
	  {
	  	ans=((f1+f2+1)>>1);
	  	D=0;
	  	if(f1<f2)
	  	  X0=a[1]-(((f1+f2+1)>>1)-f1);
	  	else
	  	  X0=a[1]+(f1-((f1+f2+1)>>1));
	  }
	for(int d=1;d<=lim;++d)
	  {
	  	f1=f2=0;
	  	for(int i=2;i<=n;++i)
	  	  {
	  	  	b[i]+=(i-1);
	  	  	if(b[i]>0) f2=max(f2,b[i]);
			else f1=max(f1,-b[i]);
	  	  }
	  	if(ans>((f1+f2+1)>>1))
	  	  {
	  	  	ans=((f1+f2+1)>>1);
	  		D=d;
		  	if(f1<f2)
	  		  X0=a[1]-(((f1+f2+1)>>1)-f1);
	 	 	else
	 	 	  X0=a[1]+(f1-((f1+f2+1)>>1));
	  	  }
	  }
	printf("%d
%d %d
",ans,X0,D);
	return 0;
	}
	for(int i=1;i<=n;++i) scanf("%d",&a[i]);
	sort(a+1,a+n+1);
	f1=f2=0;
	for(int i=1;i<=n;++i)
	  {
	  	b[i]=a[1]-a[i];
	  	if(b[i]>0) f2=max(f2,b[i]);
		else f1=max(f1,-b[i]);
	  }
	if(ans>((f1+f2+1)>>1))
	  {
	  	ans=((f1+f2+1)>>1);
	  	D=0;
	  	if(f1<f2)
	  	  X0=a[1]-(((f1+f2+1)>>1)-f1);
	  	else
	  	  X0=a[1]+(f1-((f1+f2+1)>>1));
	  }
	if(f2>=f1)
	  {
	  	printf("%d
%d %d
",ans,X0,D);
	  	return 0;
	  }
	int sz=sqrt(n),last=0;
	for(int i=1;last<=n;i+=sz)
	  {
	  	f1=f2=0;
	  	for(int j=2;j<=n;++j)
	  	  {
	  	  	b[j]+=(i==1?1:sz)*(j-1);
	  	  	if(b[j]>0) f2=max(f2,b[j]);
			else f1=max(f1,-b[j]);
	  	  }
	  	if(ans>((f1+f2+1)>>1))
	  	  {
		  	ans=((f1+f2+1)>>1);
		  	D=i;
		  	if(f1<f2)
		  	  X0=a[1]-(((f1+f2+1)>>1)-f1);
		  	else
		  	  X0=a[1]+(f1-((f1+f2+1)>>1));
		  }
	  	if(f2>=f1)
	  	  {
	  	  	for(int j=2;j<=n;++j)
	  	  	  b[j]-=(i==1?1:sz)*(j-1);
	  	  	for(int j=last+1;j<=i;++j)
	  	  	  {
	  	  	  	f1=f2=0;
	  	  		for(int k=2;k<=n;++k)
	  	 	  	  {
	  	  			b[k]+=(k-1);
	  	 	 		if(b[k]>0) f2=max(f2,b[k]);
					else f1=max(f1,-b[k]);
	  			  }
	  			if(ans>((f1+f2+1)>>1))
	  			  {
	  				ans=((f1+f2+1)>>1);
	  				D=j;
	  				if(f1<f2)
	  	  			  X0=a[1]-(((f1+f2+1)>>1)-f1);
	  				else
	  	  			  X0=a[1]+(f1-((f1+f2+1)>>1));
	  			  }
	  	  	  	if(f2>=f1)
			      {
			  	    printf("%d
%d %d
",ans,X0,D);
			  	    return 0;
			      }
	  	  	  }
	  	  }
	  	last=i;
	  }
	return 0;
}
原文地址:https://www.cnblogs.com/autsky-jadek/p/4339816.html