题解 P5050 【【模板】多项式多点求值】

AThousandSuns

2019-01-30 12:03:56

Solution

调了一个月终于调出来了…… 这里就加点详细的注释表明哪里容易写错好了。毕竟没题解调这种题会死人的…… --- 我们发现如果构造一个多项式 $P_{l,r}(x)=\prod\limits^r_{i=l}(x-a_i)$,那么对于所有的 $l\le i\le r$,都有 $P_{l,r}(a_i)=0$。 存在一个 $n-(r-l+1)$ 次多项式 $Q$,和 $r-l$ 次多项式 $R$ 满足 $F(a_i)=P_{l,r}(a_i)Q(a_i)+R(a_i)$。因为 $P_{l,r}(a_i)=0$,所以 $F(a_i)=R(a_i)$。我们便成功把一个 $n$ 次多项式变成了 $r-l$ 次多项式。 求 $R(a_i)$?发现它实际上是 $F$ 模 $P_{l,r}$,多项式除法/取模! 现在考虑分治。令 $F_{l,r}(x)$ 为当前区间的多项式,那么分成 $[l,mid]$ 和 $[mid+1,r]$ 计算。以 $[l,mid]$ 为例,$F_{l,r}(a_i)=P_{l,mid}(a_i)Q(a_i)+F_{l,mid}(a_i)$。 边界:$l=r$ 时 $F$ 次数为 $0$,那么 $F(a_i)=[x^0]F_{l,r}(x)$。 但是 $P_{l,r}(x)$ 直接求也不可承受。实际上我们可以另外再来一次分治,$P_{l,r}(x)=P_{l,mid}(x)P_{mid+1,r}(x)$。边界也是 $l=r$。 时间复杂度:$T(n)=2T(\frac{n}{2})+O(n\log n)=O(n\log^2n)$。 因为自带大常数,我就模仿了@officeyutong 的做法,在 $r-l\le100$ 的时候直接秦九韶爆算。实际上会跑得快很多。 下面是我的代码+注释:(我用vector写的,可能会比较丑) ```cpp #include<bits/stdc++.h> using namespace std; const int mod=998244353,g=3; #define lson o<<1,l,mid #define rson o<<1|1,mid+1,r #define FOR(i,a,b) for(int i=(a);i<=(b);i++) #define ROF(i,a,b) for(int i=(a);i>=(b);i--) inline int read(){ int x=0,f=0;char ch=getchar(); while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar(); while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar(); return f?-x:x; } int n,m,b[64444],ans[64444]; int limit,l,rev[266666]; vector<int> poly[266666],a; inline void init(int upr){ for(limit=1,l=0;limit<=upr;limit<<=1,l++); FOR(i,0,limit-1) rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1)); } inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;} inline int sub(int x,int y){return x<y?x-y+mod:x-y;} inline int qpow(int a,int b){ int ans=1; for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) ans=1ll*ans*a%mod; return ans; } void NTT(vector<int> &A,int tp){ while(A.size()<limit) A.push_back(0); //加上这话!不然会越界! FOR(i,0,limit-1) if(i<rev[i]) swap(A[i],A[rev[i]]); for(int i=1;i<limit;i<<=1) for(int j=0,r=i<<1,Wn=qpow(g,mod-1+tp*(mod-1)/r);j<limit;j+=r) for(int k=0,w=1;k<i;k++,w=1ll*w*Wn%mod){ int x=A[j+k],y=1ll*A[i+j+k]*w%mod; A[j+k]=add(x,y);A[i+j+k]=sub(x,y); } if(tp==-1){ int linv=qpow(limit,mod-2); FOR(i,0,limit-1) A[i]=1ll*A[i]*linv%mod; } } void NTT(vector<int> A,vector<int> B,vector<int> &C){ C.clear(); //清空! NTT(A,1);NTT(B,1); FOR(i,0,limit-1) C.push_back(1ll*A[i]*B[i]%mod); //不能直接用下标! NTT(C,-1); } void inv(vector<int> A,vector<int> &B,int deg){ //逆元(必须保证调用时B是空的) if(deg==1) return B.push_back(qpow(A[0],mod-2)); inv(A,B,(deg+1)>>1); init(deg<<1); while(A.size()<limit) A.push_back(0); //虽然NTT内有补全到limit的话,但这也要写 FOR(i,deg,limit-1) A[i]=0; //因为这里需要把deg后的设为0 NTT(A,1);NTT(B,1); FOR(i,0,limit-1) B[i]=1ll*sub(2,1ll*A[i]*B[i]%mod)*B[i]%mod; NTT(B,-1); FOR(i,deg,limit-1) B[i]=0; } void modulo(vector<int> A,vector<int> B,vector<int> &D,int n,int m){ //取模(须保证B高位为空,A随便) while(A.size()<=n) A.push_back(0); while(B.size()<=m) B.push_back(0); //把空位补全!(具体原因下文会说) if(n<m) return void(D=A); //如果除不了,那么答案就是被除数! vector<int> Arev,Brev,Brevinv,Crev,C;D.clear(); FOR(i,0,n) Arev.push_back(A[n-i]); FOR(i,0,m) Brev.push_back(B[m-i]); FOR(i,n-m+2,Arev.size()-1) Arev[i]=0; FOR(i,n-m+1,Brev.size()-1) Brev[i]=0; //记得清零 inv(Brev,Brevinv,n-m+1); init((n-m+1)<<1); NTT(Arev,Brevinv,Crev); FOR(i,0,n-m) C.push_back(Crev[n-m-i]); init(n<<1); NTT(B,C,D); FOR(i,0,m-1) D[i]=sub(A[i],D[i]); FOR(i,m,limit-1) D[i]=0; } void get_poly(int o,int l,int r){ if(l==r) return void((poly[o].push_back(b[l]?mod-b[l]:0),poly[o].push_back(1))); //l=r时就是x-a_l int mid=(l+r)>>1; get_poly(lson);get_poly(rson); //分治两边 init(r-l+1); NTT(poly[o<<1],poly[o<<1|1],poly[o]); //乘到自己这 } void solve(vector<int> A,int o,int l,int r){ if(r-l<=100){ //常数(?)优化 FOR(i,l,r){ int s=0; ROF(j,A.size()-1,0) s=add(1ll*s*b[i]%mod,A[j]); //直接秦九韶 ans[i]=s; } return; } vector<int> B;int mid=(l+r)>>1; modulo(A,poly[o<<1],B,r-l,mid-l+1); //取模到左边 //此时A的次数应该<=r-l solve(B,lson); modulo(A,poly[o<<1|1],B,r-l,r-mid); //取模到右边 //因为A一开始的次数(就是n)可能巨小无比,甚至<r-l,所以在取模中要把高位补齐,不然可能越界 solve(B,rson); } int main(){ n=read();m=read(); FOR(i,0,n) a.push_back(read()); FOR(i,1,m) b[i]=read(); get_poly(1,1,m); modulo(a,poly[1],a,n,m); //提前取模(注意!就是这里可能n<m,所以在取模中就要特判) solve(a,1,1,m); FOR(i,1,m) printf("%d\n",ans[i]); } ```