[卷积系列] P3723 [AH2017/HNOI2017]礼物

P3723 [AH2017/HNOI2017]礼物

性质:给一个手环加 相当于给另一个手环减
由于 m < = 100 m<=100 m<=100 所以令增加量为 c c c(可正可负), c ∈ [ − m , m ] c∈ [-m,m] c[mm]

设 一数列为 a a a ,另一列旋转以后的数列为 b b b , 那么费用为:
∑ i = 1 n ( a i + c − b i ) 2 \sum_{i=1}^n\left(a_i+c-b_i\right)^2 i=1n(ai+cbi)2

我们把第i项拿出来拆开,得到:
( a i + c − b i ) 2 = a i 2 + b i 2 + c 2 + 2 a i c − 2 a i b i − 2 b i c \left(a_i+c-b_i\right)^2=a_i^2+b_i^2+c^2+2a_ic-2a_ib_i-2b_ic (ai+cbi)2=ai2+bi2+c2+2aic2aibi2bic
\text{}
\text{}
那么原式变成了
∑ i = 1 n a i 2 + ∑ i = 1 n b i 2 + n c 2 + 2 c ( ∑ i = 1 n a i − ∑ i = 1 n b i ) − 2 ∑ i = 1 n a i b i \sum_{i=1}^na_i^2+\sum_{i=1}^nb_i^2+nc^2+2c\left(\sum_{i=1}^na_i-\sum_{i=1}^nb_i\right)-2\sum_{i=1}^na_ib_i i=1nai2+i=1nbi2+nc2+2c(i=1naii=1nbi)2i=1naibi

性质:无论怎么旋转,和不变
c c c 可以暴枚,也可以算二次函数最值
\text{}
所以只有最后一项会改变的

问题转化为求 ∑ i = 1 n a i b i \sum_{i=1}^na_ib_i i=1naibi 的最大值

把数列 a a a 反过来,变成 ∑ i = 1 n a n − i + 1 b i \sum_{i=1}^na_{n-i+1}b_i i=1nani+1bi
\text{}
a n − i + 1 相 当 于 多 项 式 A   的 n − i + 1 次 项 系 数 a_{n-i+1}相当于 多项式A \ 的 n-i+1次项系数 ani+1A ni+1 b i 相 当 于 多 项 式 B 的   i   次 项 系 数 b_i相当于 多项式B 的\ i \ 次 项系数 biB i 
∑ i = 1 n a n − i + 1 b i 就 是 多 项 式 卷 积 之 后 n + 1 次 项 的 系 数 \sum_{i=1}^na_{n-i+1}b_i 就是 多项式卷积之后 n+1次项 的系数 i=1nani+1bin+1

\text{}

将数列 b b b (或者反过来后的 a a a ) 倍长 ,用 b i + k b_{i+k} bi+k表示平移了 k k k

在这里插入图片描述
那就是对于 k ∈ [ 0 , n − 1 ] k∈[0,n-1] k[0n1] m i n ( ∑ i = 1 n a n − i + 1 b i + k ) min( \sum_{i=1}^na_{n-i+1}b_{i+k}) min(i=1nani+1bi+k)
就是求对于 k ∈ [ 0 , n − 1 ] k∈[0,n-1] k[0n1] m i n ( 卷 积 后 第 n + k + 1 次 项 的 系 数 ) min( 卷积后第 n+k+1 次项的系数) min(n+k+1)
\text{}
\text{}

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const double pi=acos(-1.0);
const int N=401000;
//int a[N],b[N],c[N<<1],rev[N<<3],limit;
int a[N],b[N],c[N],rev[N],limit;
struct comp{
	double x,y;
	comp(double xx=0,double yy=0) { x=xx;y=yy; }
	friend comp operator + (const comp &a,const comp &b) { return comp(a.x+b.x , a.y+b.y); }
	friend comp operator - (const comp &a,const comp &b) { return comp(a.x-b.x , a.y-b.y); }
	friend comp operator * (const comp &a,const comp &b) { return comp(a.x*b.x-a.y*b.y , a.y*b.x + a.x*b.y); }
}A[N],B[N];//A[N<<3],B[N<<3];//limit最大6*N 

int minn(int x,int y) { return x<y?x:y; }
void fft(comp *a,double f)
{
	for(int i=0;i<limit;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
	for(int mid=1;mid<limit;mid<<=1)
	{
		comp wn=comp(cos(pi/mid),f*sin(pi/mid));
		for(int L=0,R=(mid<<1);L<limit;L+=R){
			comp w=comp(1.0,0.0);
			for(int k=0;k<mid;k++,w=w*wn)
			{
				comp A1=a[L+k],A2=w*a[L+k+mid];
				a[L+k]=A1+A2;
				a[L+k+mid]=A1-A2;
			}
		}
	}
}
int main()
{
	int n,m,a1=0,a2=0,b1=0,b2=0;scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),a1+=a[i]*a[i],a2+=a[i];
	for(int i=1;i<=n;i++) scanf("%d",&b[i]),b1+=b[i]*b[i],b2+=b[i];
	for(int i=1;i<=n;i++) c[i]=a[n-i+1],b[i+n]=b[i];
	
	int nn=3*n,l=0;//c:2n b:n -- limit:3n
	limit=1;
	while(limit<nn) limit<<=1,l++;
	for(int i=1;i<limit;i++) rev[i]=(rev[i>>1]>>1)|(i&1)<<(l-1);
	for(int i=0;i<limit;i++) A[i]=comp(1.0*c[i],0.0);
	for(int i=0;i<limit;i++) B[i]=comp(1.0*b[i],0.0);
	 
	fft(A,1);
	fft(B,1);
	for(int i=0;i<limit;i++) A[i]=A[i]*B[i];
	fft(A,-1);

	int ans=2147483647;
	for(int i=1;i<=n;i++)
		for(int c=-m;c<=m;c++)
			ans=minn(ans,a1+b1+n*c*c + 2*c*(a2-b2) -2*(int)(A[i+n].x/limit+0.5));
	printf("%d",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值