伪造队形(FFT)


题目描述
Tukkun带着他的合唱队去环形音乐厅参加演出。上场前,Tukkun发现了严重的问题:音乐厅的工作人员把他们的合唱队形搞错了。
具体来说,Tukkun的合唱队有N个人围成一圈,身高按照顺时针顺序记为a[0],a[1],...,a[n-1]。音乐厅的工作人员则以为他们的身高是b[0],b[1],...,b[n-1]。
Tukkun只剩最后一点点时间了。由于舞台是环形的,在保证相对位置不变的前提下,Tukkun可以让他的队员循环移动若干个身位。
另外,考虑到Tukkun带的队伍里都是小朋友,音乐厅可以将整个舞台整体抬高若干厘米,视为所有队员身高增加同样的数值。提高的高度只能为整数厘米。
以上两种操作可以进行任意次。

Tukkun的目标是让新的队形看起来像工作人员安排的队形。
对于每个位置,定义这个位置的得分为身高偏差值的平方。这个偏差值就是这个位置上站的队员身高与工作人员安排身高的差值。
你的目标当然是让所有位置的得分之和最低了。


输入
第一行是两个数n,表示合唱队的人数。
第二行n个数a[i],表示Tukkun的合唱队员身高。
第三行n个数b[i],表示工作人员以为的合唱队员身高。
输出
一行,最低得分。

样例输入
5
120 130 140 150 160
180 180 200 180 180
样例输出
520
样例解释
首先循环移动2个身位,使得队形变为140 150 160 120 130。
之后将舞台垫高44厘米,最终队形为184 194 204 164 174。
每个位置的分数是16 196 16 256 36,总和520。

数据范围
20%:n<=200
40%:n<=3000
100%:n<=40000,所有身高值<=200

思路:将式子化开,化简可得

要求最小值为f-2*g

其中 f(c)=Σ(a[i]^2+b[i]^2+2*c*(a[i]-b[i])+c^2),g(s)=Σ(b[i]*a[(i+s)%n])

因为调整身高不会超过200,因此,f可以枚举得到最值,而g是一个卷积形式,我们用2*n和n的数组求卷积,最终得到的3n数组中间n个数字都是合法方案,在这之间选一个最小的即可。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<cstring>
 5 #include<algorithm>
 6 #include<complex>
 7 #define ll long long
 8 typedef std::complex<double> cd;
 9 const double PI=acos(-1);
10 ll suma,sumb,sqra,sqrb,A[500005],B[500005],a[500005],b[500005],c[500005];
11 int n,lc,pos;
12 long long calc(int h){
13     return (ll)sqra+sqrb+n*h*h+2*suma*h-2*sumb*h;
14 }
15 long long F(){
16     long long res=1000000000000LL;
17     for (int i=0;i<=201;i++){
18      res=std::min(res,calc(i));
19     }
20     return res; 
21 }
22 int bitrev(int t,int n){
23     int res=0;
24     for (int i=0;i<n;i++) res|=(t>>(n-i-1)&1)<<i;
25     return res;
26 }
27 void fft(cd *a,int n,int rev){
28     int len=1<<n;
29     static cd y[500005];
30     for (int i=0;i<len;i++) y[i]=a[bitrev(i,n)]; 
31     for (int d=1;d<len;d<<=1){
32         cd wn(exp(cd(0,PI*rev/d)));
33         for (int k=0;k<len;k+=d*2){
34             cd w(1,0);
35             for (int i=k;i<k+d;i++,w*=wn){
36                 cd u=y[i],v=y[i+d]*w;
37                 y[i]=u+v;
38                 y[i+d]=u-v;
39             }
40         }
41     }
42     if (rev==-1)
43     for (int i=0;i<len;i++) y[i]/=len;
44     for (int i=0;i<len;i++) a[i]=y[i];
45 }
46 void mul(ll *a,int la,ll *b,int lb,ll *c,int &lc){
47     int n=0,len=1;
48     static cd t1[500005],t2[500005];
49     for (;len<la*2||len<lb*2;len<<=1,n++);
50     for (int i=0;i<la;i++) t1[i]=cd(a[i],0);
51     for (int i=0;i<lb;i++) t2[i]=cd(b[i],0);
52     for (int i=la;i<len;i++) t1[i]=cd(0,0);
53     for (int i=lb;i<len;i++) t2[i]=cd(0,0);
54     fft(t1,n,1);fft(t2,n,1);
55     for (int i=0;i<len;i++) t1[i]*=t2[i];
56     fft(t1,n,-1);
57     lc=len;
58     for (int i=0;i<len;i++) c[i]=(ll)(t1[i].real()+0.5);
59 }
60 long long G(){
61     for (int i=0;i<n;i++) A[i]=A[i+n]=a[n-i-1];
62     for (int i=0;i<n;i++) B[i]=b[i];
63     mul(A,2*n,B,n,c,lc);
64     long long res=0;
65     for (int i=n;i<2*n;i++)
66      res=std::max(res,c[i]);
67     return res; 
68 }
69 int main(){
70     scanf("%d",&n);
71     for (int i=0;i<n;i++) scanf("%lld",&a[i]);
72     for (int i=0;i<n;i++) suma+=a[i],sqra+=a[i]*a[i];
73     for (int i=0;i<n;i++) scanf("%lld",&b[i]);
74     for (int i=0;i<n;i++) sumb+=b[i],sqrb+=b[i]*b[i];
75     long long ans=F();
76     ans-=2*G();
77     printf("%lld
",ans);
78 }
原文地址:https://www.cnblogs.com/qzqzgfy/p/5553525.html