多项式学习笔记(施工中)

前言:菜狗痛改前非决定认真学习多项式。

礼物

大意:

给定两个环形矩阵A,B,现在可以对A矩阵统一加上某一个值,并将其任意旋转,求操作过后\sum (a[i]-b[i])^{2}的最小值。

思路:

算是一道多项式板子题了。首先考虑将a[i]加上一个值x,那么原来的式子就可以转化为\sum(a[i]+x-b[i])^{2}=\sum a[i]^{2}+\sum b[i]^{2}+n*x^{2}+2x\sum (a[i]-b[i]) -2\sum(a[i]*b[i])

如果我们把x看做是自变量的话,这其实就是一个关于x的二次函数,那么求它的最小值不是轻轻松松嘛,而且我们发现跟x有关的项的系数都是定值,所以x的确定值是可以直接得到的。最后,我们就只需要求最后一项的最大值就可以了。

怎么求呢,如果我们把a数组倒过来的话,那么原式就可以转化为

\sum_{i=1}^{n}a[n-i+1]*b[i],那么这不就是一个卷积的形式嘛,最后考虑到a数组可以任意旋转,我们就只需要将其扩大到原来的两倍然后去卷积,最后在第n+1项到第n*2项之间取最大值就可以了。

卷积的话可以用fft,当然其实极限情况下中间变量也不会超过998244353,所以用ntt也是可以的。

算是一道比较温柔的推柿子题了。

(ps:c++是默认向0取整的,但是我们对正负情况的取整方向是不一样的,所以我们要分类讨论)

code:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define IL inline
#define ull unsigned long long
#define clr(f,n) memset(f,0,sizeof(int)*(n))
#define cpy(f,g,n) memcpy(f,g,sizeof(int)*(n))
const int _G=3,mod=998244353,Maxn=1e5+10;
namespace FastIOT{
	const int bsz=1<<18;
	char bf[bsz],*hed,*tail;
	inline char gc(){if(hed==tail)tail=(hed=bf)+fread(bf,1,bsz,stdin);if(hed==tail)return 0;return *hed++;}
	template<typename T>IL void read(T &x){T f=1;x=0;char c=gc();for(;c>'9'||c<'0';c=gc())if(c=='-')f=-1;
	for(;c<='9'&&c>='0';c=gc())x=(x<<3)+(x<<1)+(c^48);x*=f;}
	template<typename T>IL void print(T x){if(x<0)putchar(45),x=-x;if(x>9)print(x/10);putchar(x%10+48);}
	template<typename T>IL void println(T x){print(x);putchar('\n');}
}
using namespace FastIOT;
int F[Maxn<<2],FF[Maxn<<2],G[Maxn<<2],n,m;
int pfa,pfb,det;
ll powM(ll a,ll t=mod-2)
{
    ll ans=1;
    while(t)
    {
    	if(t&1)ans=ans*a%mod;
    	a=a*a%mod;t>>=1;
    }
	return ans;
}
const int invG=powM(_G);
int tr[Maxn<<2],tf;
void tpre(int n)
{
  	if (tf==n)return ;tf=n;
  	for(int i=0;i<n;i++)
    tr[i]=(tr[i>>1]>>1)|((i&1)?n>>1:0);
}
void NTT(int *g,bool op,int n)
{
    tpre(n);
    static ull f[Maxn<<2],w[Maxn<<2]={1};
    for (int i=0;i<n;i++)f[i]=(((ll)mod<<5)+g[tr[i]])%mod;
    for(int l=1;l<n;l<<=1)
    {
    	ull tG=powM(op?_G:invG,(mod-1)/(l+l));
    	for (int i=1;i<l;i++)w[i]=w[i-1]*tG%mod;
    	for(int k=0;k<n;k+=l+l)
        for(int p=0;p<l;p++)
	    {
	        int tt=w[p]*f[k|l|p]%mod;
	        f[k|l|p]=f[k|p]+mod-tt;
	        f[k|p]+=tt;
        }      
    	if (l==(1<<10)) for (int i=0;i<n;i++)f[i]%=mod;
    }
    if (!op)
    {
    	ull invn=powM(n);
    	for(int i=0;i<n;++i)
        g[i]=f[i]%mod*invn%mod;
    }
    else for(int i=0;i<n;++i)g[i]=f[i]%mod;
}
void px(int *f,int *g,int n)
{for(int i=0;i<n;++i)f[i]=1ll*f[i]*g[i]%mod;}
void times(int *f,int *g,int len,int lim)
{
    static int sav[Maxn<<2];
    int n=1;for(n;n<len+len;n<<=1);
    clr(sav,n);cpy(sav,g,n);
    NTT(f,1,n);NTT(sav,1,n);
    px(f,sav,n);NTT(f,0,n);
    clr(f+lim,n-lim);clr(sav,n);
}
void solve()
{
	read(n);read(m); 
	//cout<<n<<" "<<m<<endl;
	for(int i=0;i<n;++i)
	{
		read(FF[i]);
		pfa+=FF[i]*FF[i];
	}
	
	for(int i=0;i<n;++i)
	{
		read(G[i]);
		pfb+=G[i]*G[i];
		det+=FF[i]-G[i];  
	}
	
	for(int i=0;i<n;++i)
	{
		F[i]=FF[n-i-1];
		F[n+i]=F[i];
	}
	
	times(F,G,n*2,n*3);
	ll ma=0;
	for(int i=n;i<n*2;++i) ma=max(ma,(ll)F[i]);
	ma<<=1;
	det<<=1;
	double xx=-1.0*det/n/2.000;
	if(xx>0) xx=(ll)(xx+0.5);
	else xx=(ll)(xx-0.5);
	//printf("%d %f\n",det,xx);
	ll ans=0;
	ans+=pfa+pfb+xx*xx*n+det*xx;
	ans-=ma;
	printf("%lld\n",ans);
}
int main()
{
	//ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	solve();
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值