[AH2017/HNOI2017]礼物

题面在这里

题意

两个带有\(n\)个装饰物的手环,环上每个装饰物都有一个亮度\(x_i/y_i\),每个装饰物的初始亮度小于等于\(m\)
可以在任何一个环的所有装饰物的亮度同时加上一个非负整数\(c\),
也可以将手环任意旋转,但不能翻转;

在将两个手环旋转且装饰物对齐了之后,从对齐的某个位置开始逆时针方向对装饰物编号\(1,2,…,n\),其中\(n\)为每个手环的装饰物个数,则两个手环之间的差异值为

\[\sum_{i=1}^{n}(x_i-y_i)^2 \]

进行调整(亮度改造和旋转)使得两个手环之间的差异值最小,求出这个最小值。

数据范围

\[n\le 50000,m\le 100 \]

sol

70pts(\(n\le 5000\))

亮度改造的操作实际上就是在最后的表达式内部每个差值加上一个整数\(c\),
即我们要调整亮度使得

\[\sum_{i=1}^{n}(x_i-y_i+c)^2 \]

最小(\(c\)可以为任意整数)

拆一下式子

\[\sum_{i=1}^{n}(x_i-y_i+c)^2 \]

\[=\sum_{i=1}^{n}x_i^2+\sum_{i=1}^{n}y_i^2-2\sum_{i=1}^{n}x_iy_i+nc^2+2c\sum_{i=1}^{n}(x_i-y_i) \]

后面\(f(c)=nc^2+2c\sum_{i=1}^{n}(x_i-y_i)\)这个函数的系数不会受到环旋转的影响
我们设\(a=n,b=2\sum_{i=1}^{n}(x_i-y_i)\),则当\(c=-\frac{b}{2a}\)\(f(c)\)最小
但这里\(c\)只能为整数,我的做法是求出\(c=[-\frac{b}{2a}]\),
然后对\(f(c-1),f(c),f(c+1)\)取个\(min\)

前面同样只有\(-2\sum_{i=1}^{n}x_iy_i\)会受到环旋转的影响,
我们考虑如何让\(\sum_{i=1}^{n}x_iy_i\)最大

\(n\le 5000\)时,直接做\(n\)次即可

100pts (\(n\le 500000\))

考虑\(\sum_{i=1}^{n}x_iy_i\)这个式子
我们把\(y_i\)倒序处理一下,就可以得到\(\sum_{i=1}^{n}x_iy_{n-i+1}\)
这其实就是一个卷积的形式
于是我们考虑对\(x_{1...n}\)和倒序后的\(y_{n...1}\)\(FFT\)

对于\(FFT\)的结果式\(z_{1...2n-1}\)求出\(max_{i=1}^{n}(z_i+z_{n+i})\)
即是我们要求的\(max(\sum_{i=1}^{n}x_iy_i)\)
于是这题就我们就做完了

代码

#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<iomanip>
#include<cstring>
#include<complex>
#include<vector>
#include<cstdio>
#include<string>
#include<bitset>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define mp make_pair
#define pub push_back
#define puf push_front
#define pob pop_back
#define pof pop_front
#define RG register
#define il inline
using namespace std;
typedef unsigned long long ull;
typedef vector<int>VI;
typedef long long ll;
typedef double dd;
const dd eps=1e-10;
const dd pi=acos(-1);
const int N=1000000;
const int inf=1e9+7;
il ll read(){
	RG ll data=0,w=1;RG char ch=getchar();
	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
	if(ch=='-')w=-1,ch=getchar();
	while(ch<='9'&&ch>='0')data=data*10+ch-48,ch=getchar();
	return data*w;
}

struct point{dd r,i;}A[N],B[N];
point operator +(point x,point y)
{return (point){x.r+y.r,x.i+y.i};}
point operator -(point x,point y)
{return (point){x.r-y.r,x.i-y.i};}
point operator *(point x,point y)
{return (point){x.r*y.r-x.i*y.i,x.i*y.r+y.i*x.r};}

int n,m,l,a[N],b[N],r[N],ans,ret,sum,sumx,c;
il void fft(point *A,int n,int opt){
	for(RG int i=0;i<n;i++)if(i<r[i])swap(A[i],A[r[i]]);
	for(RG int i=2;i<=n;i<<=1){
		RG point w=(point){cos(2*pi/i),opt*sin(2*pi/i)};
		for(RG int j=0;j<n;j+=i){
			RG point wn=(point){1,0};
			for(RG int k=j;k<j+(i>>1);k++,wn=wn*w){
				RG point x=wn*A[k+(i>>1)];
				A[k+(i>>1)]=A[k]-x;
				A[k]=A[k]+x;
			}
		}
	}
}

int main()
{
	n=read();m=read();
	for(RG int i=0;i<n;i++)
		A[i].r=a[i]=read(),ans+=a[i]*a[i],sumx+=a[i];
	for(RG int i=n-1;~i;i--)
		B[i].r=b[i]=read(),ans+=b[i]*b[i],sumx-=b[i];
	
	for(m=n-1,n=1;n<=m+m;n<<=1,l++);
	for(RG int i=0;i<n;i++)r[i]=(r[i>>1]>>1)|((1&i)<<(l-1));
	fft(A,n,1);fft(B,n,1);
	for(RG int i=0;i<n;i++)A[i]=A[i]*B[i];
	fft(A,n,-1);
	for(RG int i=0;i<n;i++)a[i]=int(A[i].r/n+0.5);
	
	for(RG int i=0;i<m;i++)ret=max(ret,2*(a[i]+a[i+m+1]));
	ret=max(ret,2*a[m]);n=m+1;
	c=-sumx/n;c=min(n*c*c+2*sumx*c,n*(c-1)*(c-1)+2*sumx*(c-1));
	c=min(c,n*(-sumx/n+1)*(-sumx/n+1)+2*sumx*(-sumx/n+1));
	printf("%d\n",ans-ret+c);
	return 0;
}

posted @ 2018-04-13 17:18  cjfdf  阅读(133)  评论(0编辑  收藏  举报