zoukankan      html  css  js  c++  java
  • 洛谷 P6295

    洛谷题面传送门

    看到图计数的题就条件反射地认为是不可做题并点开了题解……实际上这题以我现在的水平还是有可能能独立解决的(

    首先连通这个条件有点棘手,我们尝试把它去掉。考虑这题的套路,我们设 (f_n) 表示 (n) 个点的有标号 DAG 个数,(g_n) 表示 (n) 个点的有标号且弱联通的 DAG 个数,那么根据 (exp) 式子的计算方式我们可以列出 (f,g) 生成函数之间的 exp 关系,又因为这题带标号,所以有:

    Trick 1. 对于有标号图连通图计数问题,我们可以先计算出不限制连通的方案数,这样再对求得的序列的 EGF 做一遍 (ln) 就可以得到待求序列的 EGF。

    应用到此题上,就是设 (F(x)) 表示 (f) 序列的 EGF,(G(x)) 表示 (g) 序列的 EGF,那么 (F(x)=exp(G(x))),因此我们求出 (F(x)) 后一遍 (ln) 即可还原出 (G(x))

    考虑怎么求 (F(x))。对于 DAG 有一个性质,就是剥掉它入度为 (0) 的点后仍是一个 DAG,因此我们考虑枚举入度为 (0) 的点集 (S),但是这个点集很难恰好就是入度为 (0) 的点。所以我们可以考虑:

    Trick 2. 对于 DAG 计数,我们可以考虑枚举其中入度为 (0) 的点集 (S) 并计算出剩余部分的方案数,但是这样会算重,因此需容斥,对于一个 (S) 而言其容斥系数就是 ((-1)^{|S|-1})

    因此我们枚举 (S) 的大小,有

    [f_i=sumlimits_{j=1}^idbinom{i}{j}(-1)^{j-1}2^{j(i-j)}f_{i-j} ]

    其中 (2^{j(n-j)}) 表示在钦定的入度为 (0) 的点与剩余点之间连边的方案数。

    诶呀,这里 (2^{j(i-j)}) 既涉及 (i) 又涉及 (j),怎么办呢?

    Trick 3. (2^{i-j}=2^{inom{i}{2}}·dfrac{1}{2^{inom{j}{2}}}·dfrac{1}{2^{inom{i-j}{2}}}),这样我们可以将原本与 (i,j) 都有关的东西拆成只与 (i,j,i-j) 有关的项,方便卷积

    因此

    [f_i=sumlimits_{j=1}^idfrac{i!}{j!(i-j)!}(-1)^{j-1}f_{i-j}2^{inom{i}{2}}·dfrac{1}{2^{inom{j}{2}}}·dfrac{1}{2^{inom{i-j}{2}}} ]

    整理一下

    [dfrac{f_i}{i!·2^{inom{i}{2}}}=sumlimits_{j=1}^idfrac{(-1)^{j-1}}{j!·2^{inom{j}{2}}}·dfrac{f_{i-j}}{(i-j)!·2^{inom{i-j}{2}}} ]

    到这里,式子已经可以写成分治 FFT 的形式了,可以分治 FFT 求解,时间复杂度 2log。不过注意到咱们的分治 FFT 与求逆是紧密相连的,许多分治 FFT 的题都可以写成求逆的形式,此题也不例外。设

    [P(x)=sumlimits_{i}dfrac{f_i}{i!·2^{inom{i}{2}}}x^i ]

    [Q(x)=sumlimits_{i}dfrac{(-1)^{i-1}}{i!·2^{inom{i}{2}}}(i>0) ]

    那么有 (P(x)=P(x)Q(x)+1),移个项可得 (P(x)=dfrac{1}{1-Q(x)})(1-Q(x)) 常数项显然不为 (0),因此一遍求逆即可搞定 (P(x)),也可进而求出 (F,G)

    时间复杂度 (nlog n)

    const int pr=3;
    const int ipr=332748118;
    const int MAXP=1<<18;
    const int INV2=MOD+1>>1;
    const int PHI=MOD-1;
    int fac[MAXP+5],ifac[MAXP+5],inv[MAXP+5];
    void init_fac(int n){
    	for(int i=(fac[0]=ifac[0]=inv[0]=inv[1]=1)+1;i<=n;i++) inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
    	for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%MOD,ifac[i]=1ll*ifac[i-1]*inv[i]%MOD;
    }
    int qpow(int x,int e){
    	int ret=1;
    	for(;e;e>>=1,x=1ll*x*x%MOD) if(e&1) ret=1ll*ret*x%MOD;
    	return ret;
    }
    int rev[MAXP+5];
    void NTT(vector<int> &a,int len,int type){
    	int lg=31-__builtin_clz(len);
    	for(int i=0;i<len;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<lg-1);
    	for(int i=0;i<len;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
    	for(int i=2;i<=len;i<<=1){
    		int W=qpow((type<0)?ipr:pr,(MOD-1)/i);
    		for(int j=0;j<len;j+=i){
    			for(int k=0,w=1;k<(i>>1);k++,w=1ll*w*W%MOD){
    				int X=a[j+k],Y=1ll*w*a[(i>>1)+j+k]%MOD;
    				a[j+k]=(X+Y)%MOD;a[(i>>1)+j+k]=(X-Y+MOD)%MOD;
    			}
    		}
    	} if(!~type){
    		int ivn=qpow(len,MOD-2);
    		for(int i=0;i<len;i++) a[i]=1ll*a[i]*ivn%MOD;
    	}
    }
    vector<int> conv(vector<int> a,vector<int> b){
    	int LEN=1;while(LEN<a.size()+b.size()) LEN<<=1;
    	a.resize(LEN,0);b.resize(LEN,0);NTT(a,LEN,1);NTT(b,LEN,1);
    	for(int i=0;i<LEN;i++) a[i]=1ll*a[i]*b[i]%MOD;
    	NTT(a,LEN,-1);return a;
    }
    vector<int> getinv(vector<int> a,int len){
    	vector<int> b(len,0);b[0]=qpow(a[0],MOD-2);
    	for(int i=2;i<=len;i<<=1){
    		vector<int> c(b.begin(),b.begin()+(i>>1));
    		vector<int> d(a.begin(),a.begin()+i);
    		c=conv(conv(c,c),d);
    		for(int j=0;j<i;j++) b[j]=(2ll*b[j]-c[j]+MOD)%MOD;
    	} return b;
    }
    vector<int> direv(vector<int> a,int len){
    	vector<int> b(len,0);
    	for(int i=1;i<len;i++) b[i-1]=1ll*i*a[i]%MOD;
    	return b;
    }
    vector<int> inter(vector<int> a,int len){
    	vector<int> b(len,0);
    	for(int i=1;i<len;i++) b[i]=1ll*inv[i]*a[i-1]%MOD;
    	return b;
    }
    vector<int> getln(vector<int> a,int len){
    	vector<int> _a=direv(a,len),b=getinv(a,len);
    	b=conv(b,_a);b=inter(b,len);return b;
    }
    int main(){
    	init_fac(MAXP);vector<int> f(MAXP/2),g(MAXP/2);
    	for(int i=1;i<MAXP/2;i++){
    		int val=1ll*ifac[i]*qpow(INV2,1ll*i*(i-1)/2%PHI)%MOD;
    		if(i&1) g[i]=MOD-val;else g[i]=val;
    	} (g[0]+=1)%=MOD;f=getinv(g,MAXP/2);
    //	for(int i=0;i<MAXP/2;i++) printf("%d
    ",g[i]);
    	for(int i=0;i<MAXP/2;i++) f[i]=1ll*f[i]*qpow(2,1ll*i*(i-1)/2%PHI)%MOD;
    	f=getln(f,MAXP/2);int n;scanf("%d",&n);
    	for(int i=1;i<=n;i++) printf("%d
    ",1ll*f[i]*fac[i]%MOD);
    	return 0;
    }
    
  • 相关阅读:
    POJ 3268 Silver Cow Party (Dijkstra)
    怒学三算法 POJ 2387 Til the Cows Come Home (Bellman_Ford || Dijkstra || SPFA)
    CF Amr and Music (贪心)
    CF Amr and Pins (数学)
    POJ 3253 Fence Repair (贪心)
    POJ 3069 Saruman's Army(贪心)
    POJ 3617 Best Cow Line (贪心)
    CF Anya and Ghosts (贪心)
    CF Fox And Names (拓扑排序)
    mysql8.0的新特性
  • 原文地址:https://www.cnblogs.com/ET2006/p/luogu-P6295.html
Copyright © 2011-2022 走看看