zoukankan      html  css  js  c++  java
  • 【BZOJ】3456: 城市规划

    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
    ", 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^{inom{n}{2}} - sum_{i=1}^{n-1} 2^{inom{n-i}{2}} inom{n-1}{i-1} f(i)$$

    意义很显然,所有的图减去不连通的图(这里的技巧太牛了,先取出一个点枚举这个点所在的连通块即$f(i)$,而我们可以选择$inom{n-1}{i-1}$种点与这个点在一个连通块内,然后剩下的就是随便生成图即$2^{inom{n-i}{2}}$,乘起来就好辣)

    其实这里很不好搞的,我们需要强行化简!

    发现$f(i), i<n$都在和式里,而移项后偏偏$f(n)$不在!是不是很不爽!于是去跪picks和jry!发现我们只需要左右都乘上一个$(n-1)!^{-1}$就行辣!就能提$f(n)$进和式辣!即:

    $$sum_{i=0}^{n} 2^{inom{n-i}{2}} (n-i)!^{-1} (i-1)!^{-1} f(i) = 2^{inom{n}{2}} (n-1)!^{-1}$$

    然后容易发现只要设多项式

    $$
    egin{align}
    A & = 2^{inom{i}{2}} i!^{-1} x^i \
    B & = (i-1)!^{-1} f(i) x^i \
    C & = 2^{inom{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}$即可

  • 相关阅读:
    php7垃圾回收机制
    PHP-FPM
    go main.main之前的准备
    ElementUI MessageBox 弹框
    ElementUI table 样式修改
    ElementUI Dropdown 下拉菜单
    vue+django实现下载文件
    ElementUI 点击展开/隐藏
    vue+django实现websocket连接
    ElementUI 上传文件以及限制
  • 原文地址:https://www.cnblogs.com/iwtwiioi/p/4360491.html
Copyright © 2011-2022 走看看