• luogu P5667 拉格朗日插值2 拉格朗日插值 多项式多点求值 NTT


    LINK:P5667 拉格朗日插值2

    给出了n个连续的取值的自变量的点值 求 f(m+1),f(m+2),...f(m+n).

    如果我们直接把f这个函数给插值出来就变成了了多项式多点求值 这个难度好像有点大。

    不妨 直接考虑拉格朗日插值。

    设此时要求f(k) 那么则有 (sum_{i=0}^nf(i)frac{Pi_{i eq j}(k-j)}{Pi_{i eq j} (i-j)})

    可以化简一下 (f(k)=sum_{i=0}^nf(i)frac{ Pi_{i eq j}(k-j) cdot (-1)^{k-i} }{fac_icdot fac_{n-i}})

    其实还是可以化简的 (f(k)=sum_{i=0}^nf(i)frac{ Pi (k-j) cdot (-1)^{k-i} }{fac_icdot fac_{n-i}}cdot frac{1}{k-i})

    (f(k)=sum_{i=0}^nf(i)frac{k!cdot (-1)^{k-i} }{fac_icdot fac_{n-i}}cdot frac{1}{(k-i)cdot (k-n-1)!})

    再提出来一些项 (f(k)=k!cdot frac{1}{(k-n-1)!}sum_{i=0}^nf(i)frac{(-1)^{k-i} }{fac_icdot fac_{n-i}}cdot frac{1}{(k-i)})

    容易发现这类似于卷积的形式。

    不过 如果直接做卷积会出现问题 因为卷积的时候i<=j 而并非i<=n.

    需要解决这个问题 比较容易想到 把j放到靠后的位置就能得到贡献了。

    那么其实就是 所以维护一个长度为2n的序列进行卷积即可。

    这个时候 最好的方法就是 把两个多项式写下来 看一下卷积的过程。

    分析一下每一个位置都应该是什么数字即可。

    注意 卡常的话中间有一个求2n长度逆元的东西 可以采用前缀积后缀积的方法来优化成O(n).

    但是 由于多项式长度2^20 还是跑的很慢.. 勉强卡过。

    码力还行 出错的地方是 数组开小了 开了1e6 少开了一点 wa了3,4发 我tcl.

    const int MAXN=1100010,G=3;
    int lim,n,m,N;
    int rev[MAXN];
    ll f[MAXN],b[MAXN],a[MAXN];
    ll fac[MAXN],pre[MAXN],suf[MAXN],inv[MAXN];
    inline int ksm(ll b,int p)
    {
    	ll cnt=1;
    	while(p)
    	{
    		if(p&1)cnt=cnt*b%mod;
    		p=p>>1;b=b*b%mod;
    	}
    	return cnt;
    }
    inline void NTT(ll *a,int op)
    {
    	rep(0,lim-1,i)if(i<rev[i])swap(a[i],a[rev[i]]);
    	for(int len=2;len<=lim;len=len<<1)
    	{
    		int mid=len>>1;
    		int wn=ksm(G,op==1?(mod-1)/len:mod-1-(mod-1)/len);
    		for(int j=0;j<lim;j+=len)
    		{
    			ll d=1;
    			for(int i=0;i<mid;++i)
    			{
    				ll x=a[i+j],y=a[i+j+mid]*d%mod;
    				a[i+j]=(x+y)%mod;a[i+j+mid]=(x-y+mod)%mod;
    				d=d*wn%mod;
    			}
    		}
    	}
    	if(op==-1)
    	{
    		int IN=ksm(lim,mod-2);
    		rep(0,lim-1,i)a[i]=a[i]*IN%mod;
    	}
    }
    int main()
    {
    	//freopen("1.in","r",stdin);
    	get(n);get(m);fac[0]=1;
    	rep(0,n,i)get(f[i]);
    	rep(1,n,i)fac[i]=fac[i-1]*i%mod;
    	inv[n]=ksm(fac[n],mod-2);
    	fep(n-1,0,i)inv[i]=inv[i+1]*(i+1)%mod;
    	rep(0,n,i)a[i]=(f[i]*(((n-i)&1)?-1:1)%mod*inv[i]%mod*inv[n-i]%mod+mod)%mod;
    	pre[0]=1;N=n<<1|1;suf[N+1]=1;
    	rep(1,N,i)pre[i]=pre[i-1]*(m-n+i-1)%mod;
    	fep(N,1,i)suf[i]=suf[i+1]*(m+i-n-1)%mod;
    	ll IN=ksm(pre[N],mod-2);
    	rep(1,N,i)b[i]=pre[i-1]*suf[i+1]%mod*IN%mod;
    	lim=1;while(lim<N+N)lim=lim<<1;
    	rep(0,lim-1,i)rev[i]=rev[i>>1]>>1|((i&1)?lim>>1:0);
    	NTT(a,1);NTT(b,1);
    	rep(0,lim-1,i)a[i]=a[i]*b[i]%mod;
    	ll w=1;rep(m-n,m,i)w=w*i%mod;
    	b[n+1]=w;rep(n+2,N,i)b[i]=b[i-1]*(m+i-n-1)%mod*IN%mod*pre[i-n-2]%mod*suf[i-n]%mod;
    	NTT(a,-1);rep(n+1,N,i)printf("%lld ",a[i]*b[i]%mod);return 0;
    }
    

    中间的地方看起来确实比较ex... 这也是没有办法的事情。

  • 相关阅读:
    优化慢执行或慢查询的方法
    Top K问题的两种解决思路
    优先队列实现 大小根堆 解决top k 问题
    进程间的八种通信方式----共享内存是最快的 IPC 方式
    二叉树基础之按层打印
    按层打印二叉树--每行打印一层
    给定一颗完全二叉树,给每一层添加上next的指针,从左边指向右边
    缓存与数据库一致性保证
    一致性哈希算法原理
    Linux复制指定目录下的文件夹结构
  • 原文地址:https://www.cnblogs.com/chdy/p/12701249.html
Copyright © 2020-2023  润新知