BZOJ4827: [Hnoi2017]礼物
由于之前FFT学习笔记实在太长了,就不再往里面加了
Description
我的室友最近喜欢上了一个可爱的小女生。马上就要到她的生日了,他决定买一对情侣手 环,一个留给自己,一个送给她。每个手环上各有 \(n\) 个装饰物,并且每个装饰物都有一定的亮度。但是在她生日的前一天,我的室友突然发现他好像拿错了一个手环,而且已经没时间去更换它了!他只能使用一种特殊的方法,将其中一个手环中所有装饰物的亮度增加一个相同的自然数 \(c\)(即非负整数)。并且由于这个手环是一个圆,可以以任意的角度旋转它,
但是由于上面 装饰物的方向是固定的,所以手环不能翻转。需要在经过亮度改造和旋转之后,使得两个手环的差异值最小。在将两个手环旋转且装饰物对齐了之后,从对齐的某个位置开始逆时针方向对装饰物编号 \(1,2,…,n\),其中 \(n\) 为每个手环的装饰物个数,第 \(1\) 个手环的 \(i\) 号位置装饰物亮度为 \(x_i\),第 \(2\) 个手环的 \(i\) 号位置装饰物
亮度为 \(y_i\),两个手环之间的差异值为(参见输入输出样例和样例解释): \(\sum_{i=1}^{n}(x_i-y_i)^2\) 麻烦你帮他计算一下,进行调整(亮度改造和旋转),使得两个手环之间的差异值最小, 这个最小值是多少呢?
Input
输入数据的第一行有两个数 \(n\) , \(m\) ,代表每条手环的装饰物的数量为 \(n\) ,每个装饰物的初始亮度小于等于 \(m\) 。
接下来两行,每行各有 \(n\) 个数,分别代表第一条手环和第二条手环上从某个位置开始逆时 针方向上各装饰物的亮度。
\(1≤n≤50000, 1≤m≤100, 1≤ai≤m\)
Output
输出一个数,表示两个手环能产生的最小差异值。
注意在将手环改造之后,装饰物的亮度 可以大于 \(m\)。
Sample Input
5 6
1 2 3 4 5
6 3 3 4 5
Sample Output
1
【样例解释】
需要将第一个手环的亮度增加1,第一个手环的亮度变为: 2 3 4 5 6 旋转一下第二个手环。对于该样例,是将第二个手环的亮度6 3 3 4 5向左循环移动 2017-04-15 第 6 页,共 6 页 一个位置,使得第二手环的最终的亮度为:3 3 4 5 6。此时两个手环的亮度差异值为1。
Solution:
我们根据题意,另第一个手环的增加量为 \(c\) ,第二个手环旋转过 \(t\) 个位置,最终在 \(t\) 位置的差异值为函数 \(G(t)\)。
不难把题意写成一下式子:
然后进行一些化简
这时,我们可以发现,上式中的\(\sum a^2_i + \sum b^2_{i+t}\) 是一个定值,而对于各个 \(t\) 的 \(\sum_{i=1}^{n}a_ib_{i+t}\)可以进行计算,而考虑到 \(m\) 只有 \(100\),可以枚举每一个 $c\in [0,m] $计算上式的值,并取最小值即为最终答案。
对于\(\sum_{i=1}^{n}a_ib_{i+t}\)的计算,记\(f(t)=\sum_{i=1}^{n}a_ib_{i+t}\),
将 \(a\) 反转得到 \(a^R\),则\(f(t)=\sum_{i=1}^{n}a^R_{n-i+1}b_{i+t}\),
那么 \(h(n+t+1)=f(t)=\sum_{i=1}^{n}a^R_{n-i+1}b_{i+t}\),这是一个卷积的形式,可以使用FFT
进行优化,具体操作的时候只要在 \(b\) 数组,后面复制一遍其本身,使长度达到 \(2n\)即可。
感觉m可以很大,不知道为什么只有100,可以直接暴力枚举
总时间复杂度为 \(O(mnlogn)\)
/**************************************************************
Problem: 4827
User: zzzc18
Language: C++
Result: Accepted
Time:980 ms
Memory:10980 kb
****************************************************************/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const double PI = acos(-1.0);
const int MAXN = 200000+9;
int n,m;
int loc[MAXN];
int size,bit_length;
struct C{
double x,y;
C(double _x=0,double _y=0):x(_x),y(_y){}
};
C operator + (const C &A,const C &B){
return C(A.x+B.x,A.y+B.y);
}
C operator - (const C &A,const C &B){
return C(A.x-B.x,A.y-B.y);
}
C operator * (const C &A,const C &B){
return C(A.x*B.x-A.y*B.y,A.x*B.y+A.y*B.x);
}
C A1[MAXN],B1[MAXN];
LL calc[MAXN];
LL sumA,sumB;
LL sumA2,sumB2;
int a[MAXN],b[MAXN];
void init(int len){
for(size=1,bit_length=-1;size<len;size<<=1,bit_length++);
int now=0;
for(int i=0;i<size;i++){
loc[i]=now;
for(int j=1<<bit_length;(now^=j)<j;j>>=1);
}
}
void DFT(int *A,C *re){
for(int i=0;i<size;i++)
re[i]=C(A[loc[i]],0);
for(int k=2;k<=size;k<<=1){
int len=k>>1;
C Wn(cos(2.0*PI/k),sin(2.0*PI/k));
for(int i=0;i<size;i+=k){
C W(1,0);
for(int j=0;j<len;j++){
C u=re[i+j],v=re[i+j+len]*W;
re[i+j]=u+v;
re[i+j+len]=u-v;
W=W*Wn;
}
}
}
}
void IDFT(C *A,C *re){
for(int i=0;i<size;i++)
re[i]=A[loc[i]];
for(int k=2;k<=size;k<<=1){
int len=k>>1;
C Wn(cos(2.0*PI/k),sin(-2.0*PI/k));
for(int i=0;i<size;i+=k){
C W(1,0);
for(int j=0;j<len;j++){
C u=re[i+j],v=re[i+j+len]*W;
re[i+j]=u+v;
re[i+j+len]=u-v;
W=W*Wn;
}
}
}
for(int i=0;i<size;i++)
re[i].x/=size;
}
void solve(){
init(n<<1);
DFT(a,A1);DFT(b,B1);
for(int i=0;i<size;i++)
A1[i]=A1[i]*B1[i];
IDFT(A1,B1);
for(int i=0;i<size;i++)
calc[i]=-2LL*LL(B1[i].x+0.5);
/*//DEBIG
for(int i=0;i<size;i++)
printf("%lld\n",calc[i]/(-2LL));*/
LL ans=1000000000000000;
for(int t=0;t<=n;t++){
LL val=calc[n+t+1]+sumA2+sumB2;
LL tmp=1000000000000000;
for(int i=0;i<=m;i++)
tmp=min(tmp,1LL*n*i*i+2LL*i*(sumA-sumB)+val);
ans=min(ans,tmp);
}
printf("%lld\n",ans);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
sumA+=a[i];
sumA2+=a[i]*a[i];
}
for(int i=1;i<=n;i++){
scanf("%d",&i[b]);
b[i+n]=b[i];
sumB+=b[i];
sumB2+=b[i]*b[i];
}
reverse(a+1,a+n+1);
solve();
return 0;
}