P3723 [AH2017/HNOI2017]礼物 [FFT]

P3723 [AH2017/HNOI2017]礼物

题目描述

给出两个数列x,yx, y, 长度都为NN;
定义差异值为

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

可以将任意数列加上任意自然数dd, 求最小差异值


数据范围

30%的数据满足n500,m10n≤500, m≤10
70%的数据满足n5000n≤5000
100%的数据满足1n50000,1m100,1aim1≤n≤50000, 1≤m≤100, 1≤ai≤m


Solution

假设将其中一个加上 bb
b[m,m]b ∈ [-m, m]
则差异值为
i=1N(xi+byi)2sum_{i=1}^N (x_i+b-y_i)^2
拆开得到
i=1N(xi2+b2+yi2+2bxi2xiyi2byi)sum_{i=1}^N (x_i^2+b^2+y_i^2+2bx_i-2x_iy_i-2by_i)
化简
i=1Nxi2+i=1Nyi2+2bi=1N(xiyi)+Nb22i=1Nxiyisum_{i=1}^N x_i^2 + sum_{i=1}^N y_i^2 + 2bsum_{i=1}^N (x_i-y_i) + Nb^2 -2sum_{i=1}^N x_iy_i
由于可以改变项链装饰物顺序, 所以 i=1Nxiyisum_{i=1}^N x_iy_i 不确定值
于是可以考虑枚举xx数列的所有旋转能够得到的顺序, bb的值, 再去计算答案
复杂度 O(N2M)O(N^2M)

70 3030 pts get .
继续思考怎么优化

思考 .
思考.
思考.
思考.
思考.
思考.

发现最后一项是可以O(N2)O(N^2)预处理的,
于是 7070 pts get.
继续思考怎么优化

思考.
思考.
思考.
思考.
思考.
思考.

发现最后一项 : i=1Nxiyisum_{i=1}^N x_iy_i

反转yy数列,
可以变成卷积的形式 : i=1Nxiyni+1sum_{i=1}^N x_iy_{n-i+1}

即可用 FFTFFT 求出多项式 AA :

Ai=j=1ixjynj+1A_i = sum_{j=1}^i x_jy_{n-j+1}
又想到序列环是可以旋转的, 于是 宣布这道题无解
因为已经拆开了环, 即将yy数列的长度延长为原来的2倍
若没有延长, 多项式最高次幂为An+1A_{n+1} , 是由xiyni+1x_iy_{n-i+1}, i[1,N]i ∈ [1, N] 得到的
可以想到An+2A_{n+2}是有由xiyni+2x_iy_{n-i+2} , i[1,N]i ∈ [1, N] 得到的
进而 An+kA_{n+k}是有由 xiyni+1+kx_iy_{n-i+1+k} , i[1,N]i ∈ [1, N] 得到的
于是得出结论, An+kA_{n+k}表示旋转kk次后的i=1Nxiyni+1sum_{i=1}^N x_iy_{n-i+1}
AC思路 :将y数组反转, 并且x延长1倍, 计算卷积, 最后直接计算取最优值即可


Addition

注意精度问题, double转long long需要 -exp


Code

#include<bits/stdc++.h>
#define reg register

const double Pi = acos(-1);
const int maxn = 1e6 + 10;

typedef long long ll;
int N, M, Len;
int rev[maxn];

struct Complex{
        double x, y;
        Complex(double x=0, double y=0):x(x), y(y) {}
} A[maxn], B[maxn];

Complex operator + (Complex a, Complex b){ return Complex(a.x+b.x, a.y+b.y); }
Complex operator - (Complex a, Complex b){ return Complex(a.x-b.x, a.y-b.y); }
Complex operator * (Complex a, Complex b){ return Complex(a.x*b.x-a.y*b.y, b.x*a.y+a.x*b.y); }

void FFT(Complex *F, int opt){
        for(reg int i = 0; i < Len; i ++)
                if(i < rev[i]) std::swap(F[i], F[rev[i]]);
        for(reg int p = 2; p <= Len; p <<= 1){
                int l = p >> 1;
                Complex delt(cos(Pi/l), opt*sin(Pi/l));
                for(reg int i = 0; i < Len; i += p){
                        Complex w(1.0, 0.0);
                        for(reg int k = i; k < i + l; k ++){
                                Complex Temp = F[k+l] * w;
                                F[k+l] = F[k] - Temp;
                                F[k] = F[k] + Temp;
                                w = w * delt;
                        }
                }
        }
}

int main(){
        scanf("%d%d", &N, &M);
        for(reg int i = 1; i <= N; i ++) scanf("%lf", &A[i].x), A[i+N] = A[i];
        for(reg int i = 1; i <= N; i ++) scanf("%lf", &B[i].x);
        ll base = 0, base2 = 0;
        for(reg int i = 1; i <= N; i ++) base += 1ll*A[i].x*A[i].x + 1ll*B[i].x*B[i].x;
        for(reg int i = 1; i <= N; i ++) base2 += A[i].x-B[i].x;
        for(reg int i = 1; i <= N>>1; i ++) std::swap(B[i], B[N-i+1]);
        int bit_num = 0; Len = 1;
        while(Len <= (N<<2)) Len <<= 1, bit_num ++;
        for(reg int i = 0; i <= Len; i ++) rev[i] = (rev[i>>1]>>1) | ((i&1) << bit_num-1);
        FFT(A, 1), FFT(B, 1);
        for(reg int i = 0; i <= Len; i ++) A[i] = A[i] * B[i];
        FFT(A, -1);
        for(reg int i = 0; i <= Len; i ++) A[i].x /= Len*1.0;
        ll Ans = 999999999999999999;
        for(reg int i = N+1; i <= N<<1; i ++)
                for(reg int b = -M; b <= M; b ++){
                        ll Temp = base + base2*2*b + 1ll*N*b*b - 2ll*(A[i].x-0.02);
                        Ans = std::min(Ans, Temp);
                }
        printf("%lld
", Ans);
        return 0;
}
原文地址:https://www.cnblogs.com/zbr162/p/11822665.html