[AH2017HNOI2017] 礼物(fft)

[AH2017/HNOI2017] 礼物(fft)

题目描述

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

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

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

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

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

数据范围

对于 \(30\%\) 的数据,\(n \le 500\)\(m \le 10\)

对于 \(70\%\) 的数据,\(n \le 5000\)

对于 \(100\%\) 的数据,\(1 \le n \le 50000\), \(1 \le a_i \le m \le 100\)

解法

首先我们先列出来,其实就是求\(\sum_1^n[x_i^2+y_i^2+c^2+2*c(x_i-y_i)-x_i*y_i]\)

然后惊喜的发现,\(x_i^2\)\(y_i^2\)是固定值,\(c^2+2*c(x_i-y_i)\)是一个二次函数,我们也能求出极值,而且注意他们的值是与序列顺序无关的。那么此时就剩下最后一项\(x_i*y_i\)了。我们只需要让这项最大即可。如果我们枚举对齐方式,然后再\(O(n)\)地计算其值,时间复杂度为\(O(n^2)\)。是显然我们无法接受的。

然后我们如果快速算呢,icpc澳门有一道题也用到了同样的处理,就是将其中一个数组倒置,然后进行卷积。假设我们从第0项构建多项式,那么第n-1项到第2n-1项的系数,即为我们\(On\)要求的值。此时因为用了fft,时间复杂度优化到\(O(nlogn)\)。即可通过此题。、

#include<bits/stdc++.h>
#define ll long long
#define i64 long long
using namespace std;
typedef complex<double> comp;
const int MAXN = 3000005;
comp A[MAXN], B[MAXN], ans[MAXN];
int rev[MAXN];
const comp I(0, 1);
const double PI = acos(-1);
void fft(comp F[], int N, int inv = 1)
{
    int bit = log2(N);
    for (int i = 0; i < N; ++i)
    {
        rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
        if (i < rev[i])
            swap(F[i], F[rev[i]]);
    }
    for (int l = 1; l < N; l *= 2) // 枚举合并前的区间长度
    {
        comp step = exp(inv * PI / l * I);
        for (int i = 0; i < N; i += l * 2) // 遍历起始点
        {
            comp cur(1, 0);
            for (int k = i; k < i + l; ++k)
            {
                comp g = F[k], h = F[k + l] * cur;
                F[k] = g + h, F[k + l] = g - h;
                cur *= step;
            }
        }
    }
    if (inv == -1)
        for (int i = 0; i < N; ++i)
            F[i] /= N;
}
void solve(){
    int n,m;cin>>n>>m;vector<int>a(n+1);vector<int>b(n+1);
    ll sum=0ll,temp1=0;
    for(int i=1;i<=n;i++){
        cin>>a[i];sum+=a[i]*a[i];temp1+=a[i];
    }
    for(int i=1;i<=n;i++){
        cin>>b[i];sum+=1ll*b[i]*b[i];temp1-=b[i];
    }
    int n1=-floor(1.0*temp1/n);
    int n2=-ceil(1.0*temp1/n);;
    sum+=(ll)min(n1*n1*n+2*n1*temp1,n2*n2*n+2*n2*temp1);
    m=2*n-1;n--;
    int N = 1 << int(log2(n + m) + 1);
    for(int i=0;i<=m;i++)A[i]=a[n-(i%(n+1))+1];
    for(int i=0;i<=n;i++)B[i]=b[i+1];
    fft(A, N), fft(B, N);
    for (int i = 0; i < N; ++i)ans[i] = A[i] * B[i];
    fft(ans, N, -1);
    int temp2=int(ans[n].real() + 0.1);
    for (int i = n+1; i<=n+m; ++i){
        temp2=max(temp2,int(ans[i].real() + 0.1));
    }
    cout<<max(sum-2ll*temp2,0ll)<<"\n";
}
signed main(){
    std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
    srand((unsigned)time(NULL));
    //int t;cin>>t;while(t--)
    solve();
}
posted @ 2024-03-01 22:55  magic_here_tonight  阅读(7)  评论(0)    收藏  举报