gxx_slide之Evaluation

本文详细解析了HDU4656题目的算法思路,利用多项式展开与化简技巧,将问题转化为卷积形式并采用傅立叶变换进行高效计算。此外,针对特殊模数情况,运用中国剩余定理实现数值稳定性和精度保证。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

hdu4656:
链接:http://acm.hdu.edu.cn/showproblem.php?pid=4656

题意: f(x)=n1i=0Aixi 给出 A0,A1,A2,...An1 ,对于所有的 0<=k<n ,求解 f(BC2k+D) , n<=105
分析:首先,尝试把 BC2k+D 直接带入化简
f(x)=n1i=0Ai(BC2k+d)i
用二项式定理展开
f(x)=n1i=0Aiij=0Cji(BC2k)jdij
由于内层求和当中包含k,我们可以考虑交换求和顺序,即本来是一行一行求和,现在改成一列一列求和
f(x)=n1j=0n1i=jAiCji(BC2k)jdij
先把组合数展开
f(x)=n1j=0n1i=jAii!(j!)1(ij)!1(BC2k)jdij
由于内层是只关于i的求和,我们可以把所有只包含j的项都提出去
f(x)=n1j=0(j!)1(BC2k)jn1i=jAii!(ij)!1dij
于是我们惊喜地发现内层 是一个只关于 i ij的表达式,可以用傅立叶算出,记第j项的值是 Pj
那么
f(x)=n1j=0(j!)1(BC2k)jPj
按照刚才的思路,我们仍然想办法把这个式子变成一个卷积,由于要我们求的是第k项,而且表达式中存在 C2kj 这样的项,我们可以想办法构造,使得右边的表达式是一个只关于j和k-j的函数于是
因为 2kj=k2+j2(kj)2
代入式子得
f(x)=n1j=0(j!)1BjCk2+j2(kj)2Pj
Ck2 提出,就得到了我们想要的
f(x)=Ck2n1j=0(j!)1BjCj2PjC(kj)2
再用一次傅立叶就得到了我们要求的答案
本题的考验之处在于他的模数 M=106+3 并不是一个费马素数,翻看了kuangbin的代码,发现他是通过另选两个比较大的费马素数M1和M2,由于结果 <1017 <script type="math/tex" id="MathJax-Element-12495"><10^{17}</script>,在M1*M2的范围内只有惟一解,因此可以通过中国剩余定理来计算,由于当中有乘法操作,而M1*M2实在是太大,因此需要用快速加法替代乘法取模操作
这里我选用的两个素数是302776321,330301441,可以满足maxlen=262144,即可以满足数据量在 105 级别的题目,而且乘法不会溢出longlong,可以通过这道题目

总结:这道题目可以说是比较经典的题型,多项式展开后化简成卷积的形式来加速运算,同时,这道题模数的设置也让我复习了中国剩余定理(并不复杂,有时间可以写写关于这个定理的题目),按照这道题的做法,任何val<= 1018 的fft的题目都可以用ntt来做(取两个素数),最后是我的代码,g++过了,c++tle了,不知道是什么原因

#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
using namespace std;
typedef long long Int;
const int M1=302776321,M2=330301441;
const int g1=17,g2=22;
const int M=1e6+3,Maxn=262145;
Int inv[100020];
Int fac[100020];
Int a[Maxn],b[Maxn],tp[Maxn],c[Maxn],d[Maxn];
int MM,gg;
int powmod(int x,int y,int mod)
{
    int ret=1,t=x;
    while(y){if(y&1)ret=ret*(Int)t%mod;t=t*(Int)t%mod;y>>=1;}
    return ret;
}
void rev(Int *a,int N)
{
    int i,j,k;
    for(i=1,j=N>>1;i<N-1;i++)
    {
        if(i<j)swap(a[i],a[j]);
        for(k=N>>1;j>=k;j-=k,k>>=1);j+=k;
    }
}
void dft(Int *a,int N,int flag=1)
{
    rev(a,N);
    for(int m=2;m<=N;m<<=1)
    {
        Int wm=powmod(gg,(MM-1)/m,MM);
        if(flag<0)wm=powmod(wm,MM-2,MM);
        for(int k=0;k<N;k+=m)
        {
            Int w=1;
            for(int j=k;j<k+(m>>1);j++,w=w*wm%MM)
            {
                Int u=a[j],v=a[j+(m>>1)]*w%MM;
                a[j]=(u+v)%MM;
                a[j+(m>>1)]=(u-v+MM)%MM;
            }
        }
    }
}
void mull(Int *a,Int *b,int N)
{
    dft(a,N);dft(b,N);
    for(int i=0;i<N;i++)a[i]=a[i]*b[i]%MM;
    dft(a,N,-1);
    int revn=powmod(N,MM-2,MM);
    for(int i=0;i<N;i++)a[i]=a[i]*revn%MM;
}
int op=0;
Int qmul(Int x,Int y,Int mod)
{
    Int ret=0,t=x;
    while(y){if(y&1)ret=(ret+t)%mod;t=(t+t)%mod;y>>=1;}
    return ret;
}
void mul(Int *a,Int *b,int N)
{
    MM=M1;
    gg=g1;
    for(int i=0;i<N;i++)c[i]=a[i],d[i]=b[i];
    mull(a,b,N);
    MM=M2;
    gg=g2;
    mull(c,d,N);
    int rev1=powmod(M2,M1-2,M1),rev2=powmod(M1,M2-2,M2);
    Int tt=M1*(Int)M2;
    for(int i=0;i<N;i++)a[i]=(qmul(a[i]*M2%tt,rev1,tt)+qmul(c[i]*M1%tt,rev2,tt))%tt%M;
}
int main()
{
    int n,B,C,D;
    inv[1]=inv[0]=fac[0]=fac[1]=1;
    for(int i=2;i<=100000;i++)inv[i]=inv[M%i]*(M-M/i)%M,fac[i]=fac[i-1]*i%M;
    for(int i=2;i<=100000;i++)inv[i]=inv[i-1]*inv[i]%M;
    while(scanf("%d%d%d%d",&n,&B,&C,&D)!=EOF)
    {
        int N=1;
        while(N<n)N<<=1;N<<=1;
        for(int i=0,curd=1;i<n;curd=curd*(Int)D%M,i++)
        {
            int x;scanf("%d",&x);
            a[i]=x*(Int)fac[i]%M;
            b[n-1-i]=curd*inv[i]%M;
        }
        for(int j=n;j<N;j++)a[j]=b[j]=0;
        mul(a,b,N);
        for(int i=0;i<n;i++)
        {
            tp[i]=powmod(C,(int)(i*(Int)i%(M-1)),M);
            a[i]=powmod(B,i,M)%M*tp[i]%M*inv[i]%M*a[i+n-1]%M;
        }
        for(int i=n;i<N;i++)a[i]=0;
        for(int i=0;i<n+n-1;i++)b[i]=powmod(tp[abs(i-(n-1))],M-2,M);
        for(int i=n+n-1;i<N;i++)b[i]=0;
        op=1;
        mul(a,b,N);
        for(int i=0;i<n;i++)printf("%d\n",(int)(a[i+n-1]*tp[i]%M));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值