【HNOI2017】BZOJ4827礼物题解(FFT)

题目:BZOJ4827.
题目大意:给定两个环 x , y x,y x,y,其中环 x x x可以全体权值加 c c c(一个自然数),环 y y y可以旋转角度.现在要求操作后 ∑ i = 0 n − 1 ( x i − y i ) 2 \sum_{i=0}^{n-1}(x_i-y_i)^2 i=0n1(xiyi)2最小.
1 ≤ n ≤ 5 ∗ 1 0 4 , 1 ≤ m ≤ 100 , 1 ≤ x i , y i ≤ m 1\leq n\leq 5*10^4,1\leq m\leq100,1\leq x_i,y_i\leq m 1n5104,1m100,1xi,yim.

我们把环 x x x要加上 c c c直接写出来:
∑ i = 0 n − 1 ( x i + c − y i ) 2 \sum_{i=0}^{n-1}(x_i+c-y_i)^2 i=0n1(xi+cyi)2

大力展开:
∑ i = 0 n − 1 ( x i + c − y i ) 2 = ∑ i = 0 n − 1 ( x i 2 + y i 2 + c 2 − 2 x i y i + 2 x i c − 2 y i c ) = n c 2 + ∑ i = 0 n − 1 x i 2 + ∑ i = 0 n − 1 y i 2 + 2 c ∑ i = 0 n − 1 x i − 2 c ∑ i = 0 n − 1 y i − 2 ∑ i = 0 n − 1 x i y i \sum_{i=0}^{n-1}(x_i+c-y_i)^2\\ =\sum_{i=0}^{n-1}(x_i^2+y_i^2+c^2-2x_iy_i+2x_ic-2y_ic)\\ =nc^2+\sum_{i=0}^{n-1}x_i^2+\sum_{i=0}^{n-1}y_i^2+2c\sum_{i=0}^{n-1}x_i-2c\sum_{i=0}^{n-1}y_i-2\sum_{i=0}^{n-1}x_iy_i i=0n1(xi+cyi)2=i=0n1(xi2+yi2+c22xiyi+2xic2yic)=nc2+i=0n1xi2+i=0n1yi2+2ci=0n1xi2ci=0n1yi2i=0n1xiyi

想到能够改变整个式子值的只有 c c c x i x_i xi y i y_i yi相乘形式的项.

把与 c c c有关的项写出来:
n c 2 + 2 c ( ∑ i = 0 n − 1 x i − ∑ i = 0 n − 1 y i ) nc^2+2c(\sum_{i=0}^{n-1}x_i-\sum_{i=0}^{n-1}y_i) nc2+2c(i=0n1xii=0n1yi)

发现这个式子中除了 c c c都是常量,所以考虑把这个式子最小化.发现这玩意是个二次函数,求个对称轴上下取整的值取个最小值即可.

在考虑与 x i y i x_iy_i xiyi有关的项:
− 2 ∑ i = 0 n − 1 x i y i -2\sum_{i=0}^{n-1}x_iy_i 2i=0n1xiyi

− 2 -2 2去掉,就变成了让以下式子最大化:
∑ i = 0 n − 1 x i y i \sum_{i=0}^{n-1}x_iy_i i=0n1xiyi

发现这个式子很像FFT的形式,把 y y y数组翻转一下:
∑ i = 0 n − 1 x i y n − i + 1 \sum_{i=0}^{n-1}x_iy_{n-i+1} i=0n1xiyni+1

发现这是个卷积可以用FFT优化计算.

可是我们要的是可以旋转的最大值啊,所以考虑破环成链,倍长 y y y,得到 n n n 2 n − 1 2n-1 2n1项之中的最大值既可以作为原来 ∑ i = 0 n − 1 x i y i \sum_{i=0}^{n-1}x_iy_i i=0n1xiyi的最大值.

总时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn).

代码如下:

#include<bits/stdc++.h>
using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=262144,C=20;
const double pi=acos(-1);

struct comp{
  double a,b;
  comp(double A=0,double B=0){a=A;b=B;}
  comp operator + (const comp &p){return comp(a+p.a,b+p.b);}
  comp operator - (const comp &p){return comp(a-p.a,b-p.b);}
  comp operator * (const comp &p){return comp(a*p.a-b*p.b,a*p.b+b*p.a);} 
};

int n,m,x[N+9],y[N+9],sum;

int Get_sqrfun(int a,int b,int x){return a*x*x+b*x;}

int len,rev[N+9];
comp pw[N+9],a[N+9],b[N+9];

void Get_len(int n){
  int l=0;
  for (len=1;len<=n;len<<=1) ++l;
  for (int i=0;i<len;++i) rev[i]=rev[i>>1]>>1|(i&1)<<l-1;
}

void FFT(comp *a,int n,int t){
  for (int i=0;i<n;++i)
    if (i<rev[i]) swap(a[i],a[rev[i]]);
  for (int i=1;1<<i<=n;++i){
  	int len=1<<i;
  	comp wn=comp(cos(2.0*pi/len),sin(2.0*pi/len)*(t?-1:1));
  	pw[0]=comp(1,0);
  	for (int j=1;j<len>>1;++j) pw[j]=pw[j-1]*wn;
  	for (int j=0;j<n;j+=len)
  	  for (int k=0;k<len>>1;++k){
  	  	comp x=a[j+k],y=pw[k]*a[j+k+(len>>1)];
  	  	a[j+k]=x+y;a[j+k+(len>>1)]=x-y;
  	  }
  }
}

void Poly_mul(comp *a,comp *b,int n,int m){
  Get_len(n+m);
  FFT(a,len,0);
  FFT(b,len,0);
  for (int i=0;i<len;++i) a[i]=a[i]*b[i];
  FFT(a,len,1);
  for (int i=0;i<len;++i) a[i].a=a[i].a/len+0.5;
}

int ans;

Abigail into(){
  scanf("%d%d",&n,&m);
  for (int i=0;i<n;++i){
    scanf("%d",&x[i]);
    ans+=x[i]*x[i];
    sum+=x[i];
  }
  for (int i=0;i<n;++i){
    scanf("%d",&y[i]);
    ans+=y[i]*y[i];
    sum-=y[i];
  }
}

Abigail work(){
  ans+=min(Get_sqrfun(n,sum<<1,floor(-1.0*sum/n)),Get_sqrfun(n,sum<<1,ceil(-1.0*sum/n)));
  for (int i=0;i<n;++i) a[i]=x[i],b[i]=b[i+n]=y[n-i-1];
  Poly_mul(a,b,n-1,n*2-1);
  int t=0;
  for (int i=n;i<n<<1;++i) t=max(t,(int)a[i].a);
  ans-=t<<1;
}

Abigail outo(){
  printf("%d\n",ans);
}

int main(){
  into();
  work();
  outo();
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值