• 洛谷 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;
    }
    
  • 相关阅读:
    接口自动化架构-获取用例
    Windows性能监控工具Perfmon使用指南
    接口自动化架构1-setting
    多进程
    线程锁、守护线程
    多线程
    xlrd模块
    封装写日志的类
    封装redis
    继承
  • 原文地址:https://www.cnblogs.com/ET2006/p/luogu-P6295.html
Copyright © 2020-2023  润新知