洛谷P3723 [AH2017/HNOI2017]礼物

题目描述

我的室友最近喜欢上了一个可爱的小女生。马上就要到她的生日了,他决定买一对情侣手环,一个留给自己,一个送给她。每个手环上各有 n 个装饰物,并且每个装饰物都有一定的亮度。

但是在她生日的前一天,我的室友突然发现他好像拿错了一个手环,而且已经没时间去更换它了!他只能使用一种特殊的方法,将其中一个手环中所有装饰物的亮度增加一个相同的自然数 c(即非负整数)。并且由于这个手环是一个圆,可以以任意的角度旋转它,但是由于上面装饰物的方向是固定的,所以手环不能翻转。需要在经过亮度改造和旋转之后,使得两个手环的差异值最小。

在将两个手环旋转且装饰物对齐了之后,从对齐的某个位置开始逆时针方向对装饰物编号1,2,…,n,其中 n 为每个手环的装饰物个数, 第 1 个手环的 i 号位置装饰物亮度为 xi,第 2 个手环的 i 号位置装饰物亮度为 yi,两个手环之间的差异值为(参见输入输出样例和样例解释):

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

麻烦你帮他计算一下,进行调整(亮度改造和旋转),使得两个手环之间的差异值最小,这个最小值是多少呢?

输入输出格式

输入格式:

输入数据的第一行有两个数n, m,代表每条手环的装饰物的数量为n,每个装饰物的初始亮度小于等于m。

接下来两行,每行各有n个数,分别代表第一条手环和第二条手环上从某个位置开始逆时针方向上各装饰物的亮度。

输出格式:

输出一个数,表示两个手环能产生的最小差异值。注意在将手环改造之后,装饰物的亮度

可以大于 m。

输入输出样例

输入样例#1: 
5 6
1 2 3 4 5
6 3 3 4 5
输出样例#1: 
1

说明

【样例解释】

需要将第一个手环的亮度增加1,第一个手环的亮度变为: 2 3 4 5 6

旋转一下第二个手环。对于该样例,是将第二个手环的亮度6 3 3 4 5向左循环移动一个位置,使得第二手环的最终的亮度为: 3 3 4 5 6。

此时两个手环的亮度差异值为1

【数据范围】

30%的数据满足n≤500, m≤10;

70%的数据满足n≤5000;

100%的数据满足1≤n≤50000, 1≤m≤100, 1≤ai≤m。

FFT板子题。。。

设第一个手环增加 t ,则有:

S = sigma[ ( xi - yi + t )^2 ] = sigma [ ( xi - yi )^2 ] + n * t^2 + sigma ( xi - yi ) 

所以,当 t = - sigma( xi - yi ) / n 时,S取最小值。

而 sigma (xi - yi ) 不随旋转而改变,所以求 t 复杂度为 O(1)。

又因为 E = sigma [ ( xi - yi )^2 ] = sigma [ xi^2 + yi^2 ] -2 * sigma ( xi * yi ),

sigma [ xi^2 + yi^2 ] 不随旋转而改变,且 E 要最小,所以要求 sigma ( xi * yi ) 最小。

那么,怎么求 sigma ( xi * yi ) 呢?

我们发现,将 yi 翻转,再将两个数列当成两个大数相乘,得到的每一位(没有进行进位)就是不同旋转情况的 sigma ( xi * yi ) 的值。

于是求出最大值,带入 E ,再代入 S 即可。

注意:FFT因为要转到 double 在转回 long long ,有精度误差!

而且求 t 时,要在 t-1,t,t+1 中选出使 S-E 取最小值的 t 。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define MAXN (1<<18)
using namespace std;
int n,m,x[MAXN],y[MAXN];
long long add,s1=0,s2=0,s;
namespace FFT{
	int n,l,rev[MAXN];
	long long answer=2147483646,ans[MAXN];
	struct node{
		double r,i;
		node operator +(const node &a)const{return (node){r+a.r,i+a.i};}
		node operator -(const node &a)const{return (node){r-a.r,i-a.i};}
		node operator *(const node &a)const{return (node){r*a.r-i*a.i,r*a.i+i*a.r};}
	}a[MAXN],b[MAXN],d[MAXN];
	void DFT(node *a,int f){
		for(int i=0;i<l;i++)d[i]=a[rev[i]];
		for(int i=0;i<l;i++)a[i]=d[i];
		for(int i=2;i<=l;i<<=1){
			node wi=(node){cos(2.00*M_PI/i),f*sin(2.00*M_PI/i)};
			for(int k=0;k<l;k+=i){
				node w=(node){1.00,0.00},x,y;
				for(int j=0;j<i/2;j++){
					x=a[k+j];y=w*a[k+j+i/2];
					a[k+j]=x+y;a[k+j+i/2]=x-y;
					w=w*wi;
				}
			}
		}
		if(f==-1)
		for(int i=0;i<l;i++)a[i].r/=l;
	}
	void FFT(){
		DFT(a,1);DFT(b,1);
		for(int i=0;i<l;i++)a[i]=a[i]*b[i];
		DFT(a,-1);
	}
	void init(int x){
		for(n=l=1;l<x;l<<=1)n++;
		l<<=1;
		for(int i=0;i<l;i++)
		for(int j=0,t=i;j<n;j++,t>>=1){rev[i]<<=1;rev[i]|=t&1;}
	}
	void DSC(int len){
		int l1=0,l2=0;
		for(int i=1;i<=len*2;i++){a[l1].r=x[i];a[l1++].i=0.00;}
		for(int i=1;i<=len;i++){b[l2].r=y[i];b[l2++].i=0.00;}
		reverse(a,a+l1);reverse(b,b+l2);
		init(max(l1,l2));
		FFT();
		for(int i=1;i<=l;i++)ans[i]=(long long)(a[i-1].r+0.5);
		for(int i=1;i<=l;i++)answer=min(answer,s2-ans[i]*2);//直接压缩了
		printf("%lld\n",answer+s);
	}
}
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
void init(){
	s=2147483646;
	n=read();m=read();
	for(int i=1;i<=n;i++)x[i+n]=x[i]=read();
	for(int i=1;i<=n;i++)y[n-i+1]=read();
	for(int i=1;i<=n;i++){
		s1+=x[i]-y[i];
		s2+=x[i]*x[i]+y[i]*y[i];
	}
	add=(long long)((double)-s1*1.000/n+0.5);
	for(int i=-1;i<=1;i++)s=min(s,(n*(add+i)*(add+i)+2*s1*(add+i)));
}
int main(){
	init();
	FFT::DSC(n);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值