http://www.lydsy.com/JudgeOnline/problem.php?id=3456
題意:求n個點的無向連通圖的方案。(n<=130000)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=130050, fN=N<<2; const ll mo=1004535809; ll G[35], nG[35]; int rev[fN]; ll ipow(ll a, int b) { ll x=1; for(; b; b>>=1, (a*=a)%=mo) if(b&1) (x*=a)%=mo; return x; } void fft(ll *a, int n, int f) { for(int i=0; i<n; ++i) if(i<rev[i]) swap(a[i], a[rev[i]]); int now=-1; for(int m=2; m<=n; m<<=1) { int mid=m>>1; ++now; ll wn=G[now]; if(f) wn=nG[now]; for(int i=0; i<n; i+=m) { ll w=1; for(int j=0; j<mid; ++j) { ll u=a[i+j], v=a[i+j+mid]*w%mo; a[i+j]=(u+v)%mo; a[i+j+mid]=(u-v+mo)%mo; (w*=wn)%=mo; } } } } ll tmp[fN]; void getinv(ll *A, ll *B, int n) { if(n==1) { B[0]=ipow(A[0], mo-2); return; } getinv(A, B, (n+1)>>1); int len=1, bl=-1, nn=(n<<1)-1; for(; len<nn; len<<=1, ++bl); for(int i=1; i<len; ++i) rev[i]=(rev[i>>1]>>1)|(((ll)i&1)<<bl); for(int i=0; i<n; ++i) tmp[i]=A[i]; for(int i=n; i<len; ++i) tmp[i]=0; fft(tmp, len, 0); fft(B, len, 0); for(int i=0; i<len; ++i) B[i]=B[i]*((2-tmp[i]*B[i]%mo+mo)%mo)%mo; fft(B, len, 1); ll nN=ipow(len, mo-2); for(int i=0; i<n; ++i) (B[i]*=nN)%=mo; for(int i=n; i<len; ++i) B[i]=0; } ll ni[N], p[N], A[fN], B[fN], nA[fN]; int main() { int n; scanf("%d", &n); if(n<=2) { puts("1"); return 0; } int len=1, bl=-1, nn=((n+1)<<1)-1; for(; len<nn; len<<=1, ++bl); G[bl]=ipow(3, (mo-1)/len); nG[bl]=ipow(G[bl], mo-2); for(int i=bl-1; i>=0; --i) G[i]=G[i+1]*G[i+1]%mo, nG[i]=nG[i+1]*nG[i+1]%mo; ni[1]=1; p[1]=1; p[0]=1; for(int i=2; i<=n; ++i) ni[i]=((-(mo/i)*ni[mo%i])%mo+mo)%mo; for(int i=2; i<=n; ++i) p[i]=p[i-1]*ni[i]%mo; A[0]=1, B[0]=0; ll last=1, C=1; for(int i=1; i<=n; ++i) A[i]=last*p[i]%mo, B[i]=last*p[i-1]%mo, last=last*((C<<=1)%=mo)%mo; getinv(A, nA, n+1); for(int i=1; i<len; ++i) rev[i]=(rev[i>>1]>>1)|(((ll)i&1)<<bl); fft(nA, len, 0); fft(B, len, 0); for(int i=0; i<len; ++i) (B[i]*=nA[i])%=mo; fft(B, len, 1); ll nN=ipow(len, mo-2); for(int i=0; i<=n; ++i) (B[i]*=nN)%=mo; ll pp=1; for(int i=2; i<=n; ++i) (pp*=(i-1))%=mo, (B[i]*=pp)%=mo; printf("%lld\n", B[n]); return 0; }
理論知識請orz:http://picks.logdown.com/posts/189620-inverse-element-of-polynomial
媽呀多項式除法好多地方我都寫跪了555調了好久555
fnt就是用原根來替換單位根,條件是顯然的,即:$2^k | (mo-1)$, $2^k>=n$,很容易得到$g^{\frac{mo-1}{m}} \pmod { mo }$可以替換fft中的復數根= =
證明就不證啦= =你可以對着算導證= =(很簡單的辣= =。可是
fnt如何求逆?其實也很簡單辣= =根是$n^{-1}g^{-\frac{mo-1}{m}}$,這個逆矩陣也是同復數根一樣的證法
然后各種亂搞就行辣= =
至於多項式求逆,如果想到倍增也是很顯然的...一下就能推出來辣= =(不會的就來問我辣= =qq在右邊。。歡迎辣
那么回到本題,容易得到
$$f(n) = 2^{\binom{n}{2}} - \sum_{i=1}^{n-1} 2^{\binom{n-i}{2}} \binom{n-1}{i-1} f(i)$$
意義很顯然,所有的圖減去不連通的圖(這里的技巧太牛了,先取出一個點枚舉這個點所在的連通塊即$f(i)$,而我們可以選擇$\binom{n-1}{i-1}$種點與這個點在一個連通塊內,然后剩下的就是隨便生成圖即$2^{\binom{n-i}{2}}$,乘起來就好辣)
其實這里很不好搞的,我們需要強行化簡!
發現$f(i), i<n$都在和式里,而移項后偏偏$f(n)$不在!是不是很不爽!於是去跪picks和jry!發現我們只需要左右都乘上一個$(n-1)!^{-1}$就行辣!就能提$f(n)$進和式辣!即:
$$\sum_{i=0}^{n} 2^{\binom{n-i}{2}} (n-i)!^{-1} (i-1)!^{-1} f(i) = 2^{\binom{n}{2}} (n-1)!^{-1}$$
然后容易發現只要設多項式
$$
\begin{align}
A & = 2^{\binom{i}{2}} i!^{-1} x^i \\
B & = (i-1)!^{-1} f(i) x^i \\
C & = 2^{\binom{i}{2}} (i-1)!^{-1} x^i \\
\end{align}
$$
原式就是$AB=C$辣。(后邊是我的錯誤= =本來就是不能放入$A$的= =感謝zrt神犇指出(在這里,為啥不將$(i-1)!^{-1}$放進$A$是有原因的555,一開始我放到了$A$就一直跪跪跪!因為這樣的話$A[0]=0$那么模任何多項式$x^i$都沒有關於$A$的逆元!!!媽媽壓QAQ!
然后就是$B=CA^{-1}$就是裸的除法辣= =對$A$模一下$x^{n+1}$即可