P4239 任意模数多项式乘法逆 (模板,MTT)

P4239 任意模数多项式乘法逆

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double lld;

const int N=1e6+5, mo=1e9+7;
struct MTT 
{
    struct cp 
    {
        lld x,y;
        inline void init() { x=y=0; }
        cp operator + (const cp& t) const { return {x+t.x,y+t.y}; }
        cp operator - (const cp& t) const { return {x-t.x,y-t.y}; }
        cp operator * (const cp& t) const { return {x*t.x-y*t.y,x*t.y+y*t.x}; } 
    }p1[N],p2[N],g[N];
    lld Pi=acos(-1);
    int rev[N];
    void fft(cp *a,int len,int inv)
    {
        for(int i=0;i<len;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
        for(int mid=1;mid<len;mid<<=1)
        {
            cp Wn=cp({cos(Pi/mid),inv*sin(Pi/mid)});
            for(int i=0;i<len;i+=mid*2)
            {
                cp w=cp({1,0});
                for(int j=0;j<mid;j++,w=w*Wn)
                {
                    cp x=a[i+j],y=w*a[i+j+mid];
                    a[i+j]=x+y,a[i+j+mid]=x-y;
                }
            }
        }
    }
    void mul(int *as,int *a,int n,int *b,int m,int mo) 
    {
        for(int i=0;i<n;i++) {
            int x=a[i];
            int aa=x>>15,bb=x&0x7fff;
            p1[i]={(lld)aa,(lld)bb};
            p2[i]={(lld)aa,-(lld)bb};
        }
        for(int i=0;i<m;i++) {
            int x=b[i];
            int aa=x>>15,bb=x&0x7fff;
            g[i]={(lld)aa,(lld)bb};
        }
        int len=2;
        while(len < n+m) len <<= 1;
        for(int i=0;i<len;i++) rev[i] = (rev[i>>1]>>1)|((i&1)?len>>1:0);
        fft(p1,len,1); fft(p2,len,1); fft(g,len,1);
        for(int i=0;i<len;i++) g[i].x/=len,g[i].y/=len;
        for(int i=0;i<len;i++) p1[i]=p1[i]*g[i],p2[i]=p2[i]*g[i];
        fft(p1,len,-1); fft(p2,len,-1);
    
        for(int i=0;i<m+n;i++) 
        {
            ll ans=0,a1b1=0,a2b2=0,a1b2=0,a2b1=0;
            a1b1=(ll)floor((p1[i].x+p2[i].x)/2+0.49)%mo;
            a1b2=(ll)floor((p1[i].y+p2[i].y)/2+0.49)%mo;
            a2b1=((ll)floor(p1[i].y+0.49)-a1b2)%mo;
            a2b2=((ll)floor(p2[i].x+0.49)-a1b1)%mo;
            ans=(((((a1b1<<15)%mo+(a1b2+a2b1))%mo)<<15)%mo+a2b2)%mo;
            ans+=mo; ans%=mo;
            as[i]=ans;
        }
        for(int i=0;i<len;i++) { p1[i].init(); p2[i].init(); g[i].init(); }
        //return n+m;
    }
}MT;
inline int ksm(int a,int b,int mo)
{
    int ans=1;
    while(b)
    {
        if(b&1) ans = 1ll*ans*a%mo;
        a = 1ll*a*a%mo; b >>= 1;
    }
    return ans;
}
int ta[N], tb[N];
inline void polyinv(int *a, int *b, int n) //倍增多项式求逆
{
    if(n==1) { b[0] = ksm(a[0], mo-2, mo); return ; }
    polyinv(a, b, n+1>>1);
    MT.mul(ta,a,n,b,n,mo);
    MT.mul(tb,ta,n,b,n,mo);
    for(int i=0;i<n;i++) b[i] = (b[i]+b[i])%mo;
    for(int i=0;i<n;i++) b[i] = (b[i]-tb[i]+mo)%mo;
}
int g[N], f[N];
signed main()
{
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); 
    int n;
    cin>>n;
    for(int i=0;i<n;i++) cin>>g[i];
    polyinv(g, f, n);
    for(int i=0;i<n;i++) cout<<f[i]<<' ';
    return 0;
}

P4238 【模板】多项式乘法逆

#include <bits/stdc++.h>
#define int long long 
using namespace std;

const int  N=(1<<20)+5, mo=998244353;
inline int ksm(int a,int b,int p)
{
    int ans=1;
    while(b)
    {
        if(b&1) ans = 1ll*ans*a%p;
        a = 1ll*a*a%p; b >>= 1;
    }
    return ans;
}
int rev[N];
void ntt(int *a, int len, int inv)
{
	for(int i=0;i<len;i++)
    {
		if(i < rev[i]) swap(a[i], a[rev[i]]);
    }
	for(int mid=1;mid<len;mid<<=1) 
    {	
		int Wn = ksm(3, (mo-1)/(mid<<1),mo);
        if(inv == -1) Wn = ksm(Wn, mo-2,mo);
		for(int i=0;i<len;i+=(mid<< 1)) 
        {
			int w=1;
			for(int j=0;j<mid;j++, w = (w*Wn)%mo) 
            {
				int x = a[i + j], y = w*a[i+j+mid]%mo;
				a[i+j] = (x+y)%mo; a[i+j+mid] = (x-y+mo)%mo;
			}
		}
	}
    if(inv == -1)
    {
        int fg=ksm(len, mo-2,mo);
        for(int i=0;i<len;i++) a[i] = a[i]*fg%mo;
    }
}
int ta[N], tb[N];
inline void polyinv(int *a, int *b, int n) //倍增多项式求逆
{
    if(n==1) { b[0] = ksm(a[0], mo-2, mo); return ; }
    polyinv(a, b, n+1>>1);
    int s=2;
    while(s < n*2) s <<= 1;
    for(int i=0;i<s;i++) rev[i] = (rev[i>>1]>>1)|((i&1)?(s>>1):0);
    for(int i=0;i<s;i++) ta[i] = tb[i] = 0;
    for(int i=0;i<n;i++) ta[i] = a[i];
    for(int i=0;i<n;i++) tb[i] = b[i];
    ntt(ta,s,1); ntt(tb,s,1);
    for(int i=0;i<s;i++) b[i] = tb[i]*(2ll+mo-ta[i]*tb[i]%mo)%mo;
    ntt(b,s,-1);
    for(int i=n;i<s;i++) b[i] = 0;
}
int g[N], f[N];
signed main()
{
    ios::sync_with_stdio(0); 
    int n;
    cin>>n;
    for(int i=0;i<n;i++) cin>>g[i];
    polyinv(g, f, n);
    for(int i=0;i<n;i++) cout<<f[i]<<' ';
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yezzz.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值