P3723 [AH2017/HNOI2017]礼物 快速傅里叶变换fft 推公式

题意

可以将 x i x_i xi加上任意数字 c c c,并任意旋转一手环,使得 ∑ i = 1 n ( x i − y i ) 2 \sum_{i=1}^n(x_i-y_i)^2 i=1n(xiyi)2最小

分析

先不考虑旋转:
∑ i = 1 n ( x i − y i ) 2 \sum_{i=1}^{n}(x_i-y_i)^2 i=1n(xiyi)2

∑ i = 1 n ( x i + c − y i ) 2 \sum_{i=1}^{n}(x_i+c-y_i)^2 i=1n(xi+cyi)2最小

= ∑ i = 1 n ( x i 2 + y i 2 + 2 c ( x i − y i ) − 2 x i y i + c 2 ) =\sum_{i=1}^n(x_i^2+y_i^2+2c(x_i-y_i)-2x_iy_i+c^2) =i=1n(xi2+yi2+2c(xiyi)2xiyi+c2)

= ∑ i = 1 n x i 2 + ∑ i = 1 n y i 2 + 2 c ∑ i = 1 n x i − 2 c ∑ i = 1 n y i − 2 ∑ i = 1 n x i y i + n c 2 =\sum_{i=1}^nx_i^2+\sum_{i=1}^ny_i^2+2c\sum_{i=1}^nx_i-2c\sum_{i=1}^ny_i-2\sum_{i=1}^nx_iy_i+nc^2 =i=1nxi2+i=1nyi2+2ci=1nxi2ci=1nyi2i=1nxiyi+nc2

观察柿子可以发现,出 ∑ i = 1 n x i y i \sum_{i=1}^nx_iy_i i=1nxiyi外,均为定值(关于 c c c的柿子是一个二次函数,存在极值),为了使用 f f t fft fft解决上述问题,将柿子进行化简, ∑ i = 1 n x i y i = ∑ i = 1 n x n − i + 1 y i \sum_{i=1}^nx_iy_i=\sum_{i=1}^{n}x_{n-i+1}y_i i=1nxiyi=i=1nxni+1yi,上述可以用 f f t fft fft快速求解。

由于全局最小值,相当于求上述 ∑ i = 1 n x n − i + 1 y i \sum_{i=1}^{n}x_{n-i+1}y_i i=1nxni+1yi最大值, x i , y i x_i,y_i xiyi相当于多项式 A , B A,B A,B i i i项的系数, ∑ i = 1 n x n − i + 1 y i \sum_{i=1}^{n}x_{n-i+1}y_i i=1nxni+1yi就是 n + 1 n+1 n+1项的系数。

旋转可以看做平移,将 A A A平移为两倍,答案就是 m i n [ n + 1 ∼ 2 n ] min[n+1\sim 2n] min[n+12n]项的系数

代码

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

const int N = 1e6+10, M = N * 2, inf = 1e8;
const double PI = acos(-1);
int n, m;
int rev[N], bit, tot;
struct Complex{
    double x, y;
    Complex operator+ (const Complex& t) const { return {x + t.x, y + t.y}; }
    Complex operator- (const Complex& t) const{ return {x - t.x, y - t.y}; }
    Complex operator* (const Complex& t) const{ return {x * t.x - y * t.y, x * t.y + y * t.x}; }
}a[N], b[N];

void fft(Complex a[], int inv){
    for(int i = 0; i < tot; i++)
        if(i < rev[i]) swap(a[i], a[rev[i]]);
    for(int mid = 1; mid < tot; mid <<= 1){
        Complex w1 = Complex({cos(PI/mid), inv * sin(PI/mid)});
        for(int i = 0; i < tot; i += mid * 2){
            Complex wk = Complex({1, 0});
            for(int j = 0; j < mid; j ++, wk = wk * w1){
                Complex x = a[i + j], y = wk * a[i + j + mid];
                a[i + j] = x + y, a[i + j + mid] = x - y;
            }
        }
    }
}
double A[N], B[N];
ll a2, b2, suma, sumb;
int main(){
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    cin>>n>>m;

    while((1 << bit) < 3 * n + 1) bit++;
    tot = 1 << bit;
    for(int i = 0; i < tot; i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));

    for(int i = 1; i <= n; i++) cin>>A[i], suma += A[i], a2 += A[i] * A[i];
    for(int i = 1; i <= n; i++) cin>>B[i], sumb += B[i], b2 += B[i] * B[i];

    for(int i = 1; i <= n; i++) a[i] = a[i+n] = {A[i], 0}, b[i] = {B[i], };
    reverse(b + 1, b + n + 1);

    fft(a, 1), fft(b, 1);
    for(int i = 0; i <= tot; i++) a[i] = a[i] * b[i];
    fft(a, -1);
    for(int i = 0; i <= tot; i++) a[i].x = (int)(a[i].x/tot+0.5);
    int ans = 1e9;
    for(int i = 1; i <= n; i++){
        for(int x = -m; x <= m; x++){
            int tmp = a2 + b2 + n * x * x;
            tmp += 2 * x * (suma - sumb);
            ans = min(ans, tmp - 2 * (int) a[i+n].x);
        }
    }
    cout<<ans<<endl;
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值