BZOJ4827 [Hnoi2017]礼物(洛谷P3723)

292 篇文章 1 订阅
281 篇文章 1 订阅

FFT

BZOJ题目传送门
洛谷题目传送门

咕了大半个月。。。

设旋转后两个数列分别为 {an} { a n } {bn} { b n } ,则答案为 ni=1(aibi+c)2 ∑ i = 1 n ( a i − b i + c ) 2

推式子:

==i=1n(aibi+c)2i=1n(aibi)2+2c(aibi)+c2i=1n(a2i+b2i)2i=1naibi+2i=1n(aibi)c+nc2(25)(26)(27) (25) ∑ i = 1 n ( a i − b i + c ) 2 (26) = ∑ i = 1 n ( a i − b i ) 2 + 2 c ( a i − b i ) + c 2 (27) = ∑ i = 1 n ( a i 2 + b i 2 ) − 2 ∑ i = 1 n a i b i + 2 ∑ i = 1 n ( a i − b i ) c + n c 2

ni=1(a2i+b2i) ∑ i = 1 n ( a i 2 + b i 2 ) ni=1(aibi) ∑ i = 1 n ( a i − b i ) 是个定值,而后面两个关于 c c 的可以通过二次函数对称轴求得其最小值。那么我们只需要求i=1naibi即可。

ai a i 翻转,即求 ni=1ani+1bi ∑ i = 1 n a n − i + 1 b i 。而因为它可以旋转,那么把 b b <script type="math/tex" id="MathJax-Element-101">b</script>倍长(断环为链)后做一遍FFT即可。

代码:

#include<cmath>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1<<18
#define F inline
#define sqr(x) ((x)*(x))
using namespace std;
typedef double DB;
typedef long long LL;
const DB pi=acos(-1);
struct P{ DB x,y; }a[N],b[N];
int n,m,l,r[N];
LL ans;
F char readc(){
    static char buf[100000],*l=buf,*r=buf;
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    return l==r?EOF:*l++;
}
F int _read(){
    int x=0; char ch=readc();
    while (!isdigit(ch)) ch=readc();
    while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
    return x;
}
F P operator + (P a,P b){ return (P){a.x+b.x,a.y+b.y}; }
F P operator - (P a,P b){ return (P){a.x-b.x,a.y-b.y}; }
F P operator * (P a,P b){ return (P){a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x}; }
F void FFT(P *a,int f){
    for (int i=0;i<n;i++) if (i<r[i]) swap(a[i],a[r[i]]);
    for (int k=1;k<n;k<<=1){
        P w={1,0},wn={cos(pi/k),sin(f*pi/k)},x,y;
        for (int i=0;i<n;i+=k<<1,w=(P){1,0})
            for (int j=0;j<k;j++,w=w*wn)
                x=a[i+j],y=w*a[i+j+k],a[i+j]=x+y,a[i+j+k]=x-y;
    }
}
int main(){
    n=_read(),m=_read(); DB d;
    for (int i=0;i<n;i++) a[n-i-1].x=_read();
    for (int i=0;i<n;i++) b[i].x=b[i+n].x=_read();
    for (int i=0;i<n;i++)
        ans+=1ll*sqr(a[i].x)+1ll*sqr(b[i].x),d+=a[i].x-b[i].x;
    LL L=floor(d/n),R=ceil(d/n);
    ans+=min(L*L*n-(LL)2*d*L,R*R*n-(LL)2*d*R);
    for (m=n*3,n=1;n<m;n<<=1) l++;
    for (int i=0;i<n;i++)
        r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
    FFT(a,1),FFT(b,1); LL mx=0;
    for (int i=0;i<n;i++) a[i]=a[i]*b[i];
    FFT (a,-1);
    for (int i=0;i<n;i++) mx=max(mx,(LL)(a[i].x/n+0.5));
    return printf("%lld\n",ans-2*mx),0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值