集訓隊作業2018 取名字太難了 任意模數FFT

2022-03-25 17:28:06 字數 3294 閱讀 7725

求多項式 \(\prod_^n(x+i)\) 的係數在模 \(p\) 意義下的分布,對 \(998244353\) 取模。

\(p\) 為質數。

\(n\leq ^,p\leq 250000\)

我們只計算 \([1,p-1]\) 的分布,最後再算出 \(0\) 的出現次數。

記 \(n1=\lfloor\frac\rfloor,n2=n\bmod p\)。若 \(n\bmod p=p-1\),則 \(n1=\lfloor\frac\rfloor+1,n2=0\)。

\[\begin

&\prod_^n(x+i)\\

=&x^\prod_^(x+i)^\prod_^(x+i)

\end

\]第一部分我們直接忽略。

對於第二部分,\(\prod_^(x+i)=x^-1\)(因為左右兩邊的根都是 \(1,2,\ldots,p-1\))。那麼 \(x^i(p-1)\) 項的係數就是 \(\binom^\)。

因為係數的模數為 \(p\),所以可以用 lucas 定理。

\(\binom\equiv\prod_i\binom\pmod p\)

假設 \(n1=(a_1a_2\ldots a_m)_p\)。

對每一位構造乙個多項式 \(a(x)\),\(a_i\) 表示有多少種方案滿足這一位的貢獻是 \(i\)。

那麼可以像數字dp一樣,列舉 \(i\) 的前多少位和 \(n1\) 是相同的,然後求出後面的方案數。

因為每一位都不能超過 \(a_i\),所以直接把每一位對應的多項式乘在一起就好了。

這裡的卷積稍微有點不同,\(a_i=\sum_\sum_[jk\bmod p=i]b_jc_k\),只需要把下標取離散對數就好了。

不要忘記處理 \(^\)。

複雜度為 \(o(p\log_pn\log p)=o(p\log n)\)。

對於第三部分,可以用倍增+任意模數fft求出整個多項式。

大概就是記 \(f(n,x)=\prod_^n(x+i)\),那麼 \(f(2n+1,x)=f(2n,x)(x+2n+1)\),\(f(2n,x)=f(n,x)f(n,x+n)\)。

記 \(f(n,x)=\sum_ia_ix^i\),那麼 \(f(n,x+n)=\sum_ia_i(x+n)^i=\sum_ia_i\sum_x^jn^\binom=\sum_x_i\sum_a_jn^\binom\),直接卷積即可。

複雜度為 \(o(p\log p)\)。

由於第二部分的多項式形如 \(\sum_ia_ix^\),而第三部分的次數 \(,所以兩部分的乘積不互相干擾,可以分開求出後再合併到一起。

總時間複雜度為 \(o(p(\log n+\log p))\)。

#include#include#include#include#include#include#include#include#include//using namespace std;

using std::min;

using std::max;

using std::swap;

using std::sort;

using std::reverse;

using std::random_shuffle;

using std::lower_bound;

using std::upper_bound;

using std::unique;

using std::vector;

typedef long long ll;

typedef unsigned long long ull;

typedef double db;

typedef std::pairpii;

typedef std::pairpll;

void open(const char *s)

void open2(const char *s)

int rd()dowhile((c=getchar())>='0'&&c<='9');return b?-s:s;}

void put(int x)static int c[20];int t=0;while(x)while(t)putchar(c[t--]+'0');}

int upmin(int &a,int b)return 0;}

const ll p1=998244353;

const int n=530000;

const db pi=acos(-1);

ll fp(ll a,ll b,ll p)

ll n,p,g;

ll n1,n2;

ll e1[n],e2[n];

namespace fft

}; cp operator +(cp a,cp b)

cp operator -(cp a,cp b)

cp operator *(cp a,cp b)

cp operator /(cp a,int b)

cp muli(cp a)

cp divi(cp a)

cp conj(cp a)

const int w=524288;

cp w[n];

int rev[n];

void init()

for(int i=2;i<=n;i<<=1)

for(int j=0;j=y&&y>=0?fac[x]*ifac[y]%p*ifac[x-y]%p:0;

}int check()

} return 0;

}void getg()

} else

reverse(a2,a2+(n>>1)+1);

fft::mul(a1,a2,a3,n>>1,n>>1,n>>1,p);

reverse(a3,a3+(n>>1)+1);

for(int i=0;i<=n>>1;i++)

a3[i]=a3[i]*ifac[i]%p;

fft::mul(s,a3,s,n>>1,n>>1,n,p);

} }void gao() }

namespace gao2

ans[1]=1;

for(int j=1;j<=t;j++)

}}ll ans[n];

int main()

gao1::gao();

gao2::gao();

fft::mul2(gao1::ans,gao2::ans,ans,p,p1);

ans[0]=(n+1)%p1;

for(int i=1;ians[0]=(ans[0]-ans[i])%p1;

for(int i=0;ireturn 0;

}

集訓隊作業2018 喂鴿子

設 f n 表示有 n 只鴿子,每次等概率選乙隻喂,期望餵飽第一只鴿子的時間,f 表示有 n 只鴿子,已經喂了 m 次,此時這 n 只鴿子中沒有鴿子被餵飽的概率。ans sum n 1 f i f n sum sum f frac sum f sum frac 注意到有 dfrac n sum x ...

集訓隊作業2018 小Z的禮物

小水題。題意就是不斷隨機放乙個 1 times 2 骨牌,然後取走裡面的東西。求期望多少次取走所有的東西。然後有一維很小。首先顯然 minmax 容斥,將最後取走轉化為欽定一些物品,求第乙個取走的期望。然後顯然第乙個取走的期望只和剩下能蓋到物品的骨牌數有關。乙個骨牌能蓋到物品只和相鄰的兩個格仔是否欽...

集訓隊作業2018 GAME(並查集)

題意 題解 把這個dp式子給列出來 f i si max j f i s i max j fi si jmax 把這個字尾max max max記為m mm的話,每次就是m max m max m m max,考慮對於初始的每個m mm都維護一下,發現是條折線,維護一下這個折線,每次可以把小的一半合...