1. 程式人生 > >洛谷P4238 【模板】多項式求逆(NTT)

洛谷P4238 【模板】多項式求逆(NTT)

tdi stdout style show include main -a 沒有 如果

傳送門

學習了一下大佬的->這裏

已知多項式$A(x)$,若存在$A(x)B(x)\equiv 1\pmod{x^n}$

則稱$B(x)$為$A(x)$在模$x^n$下的逆元,記做$A^{-1}(x)$

具體的來說的話,就是兩個多項式$A,B$相乘模$x^n$之後,所有次數大於等於$n$的項都沒了,那麽只有在剩下的項相乘之後未知數項全被消掉只留下一個常數項$1$時,$B$才是$A$的逆元

然後為什麽要有模$x^n$的限制呢?因為沒有這個限制的話,$B$可能有無窮多項

然後我們考慮如何計算$B(x)$

當$n=1$的時候,$A(x)\equiv c\pmod{x}$,其中$c$為常數項,那麽$A^{-1}(x)$就是$c^{-1}$

當$n>1$時$$B(x)A(x)\equiv 1\pmod{x^n}$$

設$B‘(x)$是模$x^{\left\lceil\frac{n}{2}\right\rceil}$時的逆元,即$$B‘(x)A(x)\equiv 1\pmod{x^{\left\lceil\frac{n}{2}\right\rceil}}$$

首先,可以肯定$$B(x)A(x)\equiv 1\pmod{x^{\left\lceil\frac{n}{2}\right\rceil}}$$

那麽上下兩個式子相減可得$$B(x)-B‘(x)\equiv 0\pmod{{x^{\left\lceil\frac{n}{2}\right\rceil}}}$$

然後兩邊平方$$B^2(x)+2B‘(x)B(x)+B‘^2(x)\equiv 0\pmod{{x^n}}$$

為什麽上面模數變成$x^n$呢?我們考慮如果一個多項式在$\pmod{x^n}$的情況下為$0$,那麽說明$0$到$n-1$項的系數也為$0$,它平方之後$0$到$2n-1$項系數$a_i$為$\sum_{j=0}^ia_ja_{i-j}$,那麽$j$和$i-j$中必有一個小於$n$,也就是說$a_j$和$a_{i-j}$裏必有一個為$0$,那麽$a_i$也是$0$,所以平方之後在$\mod{2n}$也為$0$

然後在上式兩邊同乘$A(x)$並移項可得$$B(x)\equiv2B‘(x)-A(x)B‘^2(x)\pmod{x^n}$$

那麽發現這個東西可以遞歸計算,時間復雜度為$O(nlogn)$

 1 //minamoto
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<algorithm>
 5 #define swap(x,y) (x^=y,y^=x,x^=y)
 6 #define mul(x,y) (1ll*x*y%P)
 7 #define add(x,y) (x+y>=P?x+y-P:x+y)
 8 #define dec(x,y) (x-y<0?x-y+P:x-y)
 9 using namespace std;
10 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
11 char buf[1<<21],*p1=buf,*p2=buf;
12 inline int read(){
13     #define num ch-‘0‘
14     char ch;bool flag=0;int res;
15     while(!isdigit(ch=getc()))
16     (ch==-)&&(flag=true);
17     for(res=num;isdigit(ch=getc());res=res*10+num);
18     (flag)&&(res=-res);
19     #undef num
20     return res;
21 }
22 char sr[1<<21],z[20];int C=-1,Z;
23 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
24 inline void print(int x){
25     if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x;
26     while(z[++Z]=x%10+48,x/=10);
27     while(sr[++C]=z[Z],--Z);sr[++C]= ;
28 }
29 const int N=(1<<21)+5,P=998244353,G=3,Gi=332748118;
30 inline int ksm(int a,int b){
31     int res=1;
32     while(b){
33         if(b&1) res=mul(res,a);
34         a=mul(a,a),b>>=1;
35     }
36     return res;
37 }
38 int n,r[N],X[N],Y[N],A[N],B[N],O[N];
39 void NTT(int *A,int type,int len){
40     int limit=1,l=0;
41     while(limit<len) limit<<=1,++l;
42     for(int i=0;i<limit;++i)
43     r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
44     for(int i=0;i<limit;++i)
45     if(i<r[i]) swap(A[i],A[r[i]]);
46     for(int mid=1;mid<limit;mid<<=1){
47         int R=mid<<1,Wn=ksm(G,(P-1)/R);O[0]=1;
48         for(int j=1;j<mid;++j) O[j]=mul(O[j-1],Wn);
49         for(int j=0;j<limit;j+=R){
50             for(int k=0;k<mid;++k){
51                 int x=A[j+k],y=mul(O[k],A[j+k+mid]);
52                 A[j+k]=add(x,y),A[j+k+mid]=dec(x,y);
53             }
54         }
55     }
56     if(type==-1){
57         //這裏這麽寫是因為如果要點值轉系數直接reverse再除以n(也就是乘個逆元)就好了 
58         reverse(A+1,A+limit);
59         for(int i=0,inv=ksm(len,P-2);i<limit;++i)
60         A[i]=mul(A[i],inv);
61     }
62 }
63 void work(int *a,int *b,int len){
64     if(len==1) return (void)(b[0]=ksm(a[0],P-2));
65     work(a,b,len>>1);
66     for(int i=0;i<len;++i) A[i]=a[i],B[i]=b[i];
67     NTT(A,1,len<<1),NTT(B,1,len<<1);
68     for(int i=0;i<(len<<1);++i)
69     A[i]=mul(mul(A[i],B[i]),B[i]);
70     NTT(A,-1,len<<1);
71     for(int i=0;i<len;++i) b[i]=(1ll*(b[i]<<1)%P+P-A[i])%P;
72 }
73 int main(){
74 //    freopen("testdata.in","r",stdin);
75     n=read();
76     for(int i=0;i<n;++i) X[i]=(read()+P)%P;
77     int len;for(len=1;len<n;len<<=1);
78     work(X,Y,len);
79     for(int i=0;i<n;++i) print(Y[i]);
80     Ot();
81     return 0;
82 }

洛谷P4238 【模板】多項式求逆(NTT)