BZOJ.4827.[HNOI2017]礼物(FFT)
Description
有两个环,上面各有n个数x1,...,xn,y1,...,yn。你可以将一个环旋转任意角度,以及将一个环中所有的数加上任意非负整数c。
旋转及修改后,将两个环从某个位置依次标号1,2,...,n,求∑ni=1(xi−yi)2的最小值。
n≤50000, xi,yi≤100。
Solution
首先不妨枚举旋转多少次k,然后题目就是要求∑ni=1(xi+k−yi+c)2的最小值,其中k∈[0,n),c∈[−m,m](显然c枚举到m就足够了,而另一个环增加可以看成这个环减少),且xi+n=xi(是个环啊)。
把式子拆开:
n∑i=1(xi+k−yi+c)2=n∑i=1[x2i+k+y2i−2xi+kyi+c2+2c(xi+k−yi)]
再把∑拆了:
n∑i=1x2i+k+n∑i=1y2i−2n∑i=1xi+kyi+nc2+2c(n∑i=1xi−n∑i=1yi)
我们发现前面两项∑ni=1x2i+k+∑ni=1y2i是不变的,后面两项nc2+2c(∑ni=1xi−∑ni=1yi)在序列已经确定的情况下只与c有关,枚举c求个最小值就可以了。
然后我们需要求∑ni=1xi+kyi的最大值,它只与k有关。
这不就和某些FFT的题一样么,反转x或y数组,变为求∑ni=1xi+kyn−i+1,就是卷积了。Ansk就是多项式相乘后的第n+k+1项。
枚举k取一个max就可以了。
//12380kb 1120ms(为啥我的FFT这么慢啊QAQ)
#include <cmath>
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=(1<<18)+5;//3n!
const double PI=acos(-1);
int X[N],Y[N],rev[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Complex
{
double x,y;
Complex(double x=0,double y=0):x(x),y(y) {}
Complex operator +(const Complex &a) {return Complex(x+a.x, y+a.y);}
Complex operator -(const Complex &a) {return Complex(x-a.x, y-a.y);}
Complex operator *(const Complex &a) {return Complex(x*a.x-y*a.y, x*a.y+y*a.x);}
}A[N],B[N];
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
void FFT(Complex *a,int lim,int opt)
{
for(int i=1; i<lim; ++i) if(i<rev[i]) std::swap(a[i],a[rev[i]]);
for(int i=2; i<=lim; i<<=1)
{
int mid=i>>1; Complex Wn(cos(PI/mid),opt*sin(PI/mid));
for(int j=0; j<lim; j+=i)
{
Complex w(1,0),t;
for(int k=j; k<j+mid; ++k,w=w*Wn)
a[k+mid]=a[k]-(t=w*a[k+mid]), a[k]=a[k]+t;
}
// for(int j=0; j<mid; ++j) W[j]=w, w=w*Wn;//预处理果然并不会快...
// for(int j=0; j<lim; j+=i)
// for(int k=0; k<mid; ++k)
// a[j+k+mid]=a[j+k]-(t=W[k]*a[j+k+mid]), a[j+k]=a[j+k]+t;
}
if(opt==-1) for(int i=0; i<lim; ++i) a[i].x/=lim;
}
int Calc(int n)
{
for(int i=1; i<=n; ++i) A[i+n]=A[i]=Complex(X[i],0);
for(int i=1; i<=n; ++i) B[i]=Complex(Y[n-i+1],0);
int lim=1,l=-1;
while(lim<=3*n) lim<<=1,++l;
for(int i=1; i<lim; ++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<l);
FFT(A,lim,1), FFT(B,lim,1);
for(int i=0; i<lim; ++i) A[i]=A[i]*B[i];
FFT(A,lim,-1);
int ans=0;
for(int k=0; k<n; ++k) ans=std::max(ans,(int)(A[n+k+1].x+0.5));
return ans;
}
int main()
{
int n=read(),m=read();
for(int i=1; i<=n; ++i) X[i]=read();
for(int i=1; i<=n; ++i) Y[i]=read();
int ans=0,s=0;
for(int i=1; i<=n; ++i) ans+=X[i]*X[i]+Y[i]*Y[i], s+=X[i]-Y[i];
int mn=2e9;
for(int c=-m; c<=m; ++c) mn=std::min(mn,n*c*c+2*c*s);
ans+=mn-2*Calc(n);
printf("%d\n",ans);
return 0;
}
------------------------------------------------------------------------------------------------------------------------
别来无恙 你在心上
------------------------------------------------------------------------------------------------------------------------
别来无恙 你在心上
------------------------------------------------------------------------------------------------------------------------
分类:
数学——FFT NTT
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
· ThreeJs-16智慧城市项目(重磅以及未来发展ai)
· .NET 原生驾驭 AI 新基建实战系列(一):向量数据库的应用与畅想
· Browser-use 详细介绍&使用文档
· 软件产品开发中常见的10个问题及处理方法
· Vite CVE-2025-30208 安全漏洞