zoukankan      html  css  js  c++  java
  • 【洛谷P3723】礼物

    题目

    题目链接:https://www.luogu.com.cn/problem/P3723
    我的室友最近喜欢上了一个可爱的小女生。马上就要到她的生日了,他决定买一对情侣手环,一个留给自己,一个送给她。每个手环上各有 \(n\) 个装饰物,并且每个装饰物都有一定的亮度。

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

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

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

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

    思路

    假设 \(a\) 旋转之后的序列为 \(c\),那么我们要求的就是

    \[\min(\sum^{n}_{i=1}(c_i-b_i+x)) \]

    拆开

    \[\sum^{n}_{i=1}(c_i^2+b_i^2)+nx^2+2x(c_i-b_i)-2\sum^{n}_{i=1}c_ib_i \]

    由于 \(m\leq 100\),所以我们可以 \(O(nm)\) 枚举出除最后一项外的最小值。所以我们只需要让 \(-2\sum^{n}_{i=1}c_ib_i\) 最小即可。
    \(c'(i)=c(n-i+1)\),那么原式就变成了一个卷积的形式。拆环为链后直接上 FFT 即可。
    时间复杂度 \(O(n\log n+nm)\)

    代码

    #include <bits/stdc++.h>
    #define cp complex<double>
    using namespace std;
    typedef long long ll;
    
    const int N=500010;
    const double pi=acos(-1);
    int n,m,lim,rev[N];
    ll fsum,gsum,fpow,gpow,ans;
    cp f[N],g[N];
    
    void fft(cp *f,int inv)
    {
    	for (int i=0;i<lim;i++)
    		if (rev[i]<i) swap(f[i],f[rev[i]]);
    	for (int mid=1;mid<lim;mid<<=1)
    	{
    		cp temp(cos(pi/mid),inv*sin(pi/mid));
    		for (int i=0;i<lim;i+=(mid<<1))
    		{
    			cp w=cp(1,0);
    			for (int j=0;j<mid;j++,w*=temp)
    			{
    				cp x=f[i+j],y=w*f[i+j+mid];
    				f[i+j]=x+y; f[i+j+mid]=x-y;
    			}
    		}
    	}
    }
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for (int i=0,x;i<n;i++)
    	{
    		scanf("%d",&x);
    		fpow+=x*x; fsum+=x;
    		f[n-i-1]=cp(1.0*x,0.0);
    	}
    	for (int i=0,x;i<n;i++)
    	{
    		scanf("%d",&x);
    		gpow+=x*x; gsum+=x;
    		g[i]=g[i+n]=cp(1.0*x,0.0);
    	}
    	lim=1; ans=1145140000000000000LL;
    	while (lim<=n*3) lim<<=1;
    	for (int i=0;i<lim;i++)
    		rev[i]=(rev[i>>1]>>1)|((i&1)?(lim>>1):0);
    	fft(f,1); fft(g,1);
    	for (int i=0;i<lim;i++)
    		f[i]*=g[i];
    	fft(f,-1);
    	for (ll j=-m;j<=m;j++)
    		for (int i=n;i<n*2;i++)
    			ans=min(ans,fpow+gpow+j*j*n+2LL*j*(fsum-gsum)-2LL*(ll)(f[i].real()/lim+0.4999));
    	printf("%lld",ans);
    	return 0;
    }
    
  • 相关阅读:
    C语言字母频率统计
    C语言文件操作相关函数
    【蓝桥杯】历届试题 回文数字
    【蓝桥杯】历届试题 蚂蚁感冒
    【蓝桥杯】历届试题 地宫取宝
    【蓝桥杯】历届试题 分糖果
    【蓝桥杯】历届试题 兰顿蚂蚁
    JDK的安装和配置
    【蓝桥杯】历届试题 错误票据
    【蓝桥杯】历届试题 带分数
  • 原文地址:https://www.cnblogs.com/stoorz/p/13681149.html
Copyright © 2011-2022 走看看