• 牛客挑战赛33 F 淳平的形态形成场(无向图计数,EGF,多项式求逆)


    传送门:

    淳平的形态形成场

    题解:

    把a排序后,直接统计答案恰好为a[i]并不好做,可以统计答案>a[i]的方案数,设为(f[i])

    即不存在一个联通块,所有的权值都<=a[i]。

    那么如果枚举j个在i之前的点,分成k个联通块,容斥系数是((-1)^k),选择系数(C_i^j),剩下的边随便乱选,(2^{(n-j)*(n-j-1)/2})

    (g[j])表示j个点,若有k个联通块,系数((-1)^k),的所有方案系数和。

    (f[i]=sum_{j=0}^iC_{i}^j*2^{(n-j)*(n-j-1)/2}*g[j])

    求出(g)之后,卷一下就可以得到f,设G为g的EGF。

    (a[i])表示i个点的简单无向图的方案数,A是它的EGF。

    (b[i])表示i个点的简单无向连通图的方案数,B是它的EGF。

    显然有(e^B=A),又有(e^B=G),所以(G=A^{-1}),妙不可言。

    Code:

    #include<bits/stdc++.h>
    #define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
    #define ff(i, x, y) for(int i = x, B = y; i <  B; i ++)
    #define fd(i, x, y) for(int i = x, B = y; i >= B; i --)
    #define ll long long
    #define pp printf
    #define hh pp("
    ")
    using namespace std;
    
    const int mo = 998244353;
    
    ll ksm(ll x, ll y) {
    	ll s = 1;
    	for(; y; y /= 2, x = x * x % mo)
    		if(y & 1) s = s * x % mo;
    	return s;
    }
    
    #define V vector<ll>
    #define si size()
    #define pb push_back
    #define re resize
    
    namespace ntt {
    	const int nm = 1 << 20;
    	int r[nm]; ll a[nm], b[nm], w[nm];
    	void bd() {
    		for(int n = 1; n < nm; n *= 2) {
    			w[n] = 1; ll v = ksm(3, (mo - 1) / 2 / n);
    			ff(j, 1, n) w[n + j] = w[n + j - 1] * v % mo;
    		}
    	}
    	void dft(ll *a, int n, int f) {
    		ff(i, 0, n) {
    			r[i] = r[i / 2] / 2 + (i & 1) * (n / 2);
    			if(i < r[i]) swap(a[i], a[r[i]]);
    		} ll b;
    		for(int i = 1; i < n; i *= 2) for(int j = 0; j < n; j += 2 * i) ff(k, 0, i)
    			b = a[i + j + k] * w[i + k], a[i + j + k] = (a[j + k] - b) % mo, a[j + k] = (a[j + k] + b) % mo;
    		if(f == -1) {
    			reverse(a + 1, a + n);
    			b = ksm(n, mo - 2);
    			ff(i, 0, n) a[i] = (a[i] + mo) * b % mo;
    		}
    	}
    	void dft(V &p, int f) {
    		int n = p.si;
    		ff(i, 0, n) a[i] = p[i];
    		dft(a, n, f);
    		ff(i, 0, n) p[i] = a[i];
    	}
    	V operator * (V p, V q) {
    		int n0 = p.si + q.si - 1, n = 1;
    		while(n < n0) n *= 2;
    		ff(i, 0, n) a[i] = b[i] = 0;
    		ff(i, 0, p.si) a[i] = p[i];
    		ff(i, 0, q.si) b[i] = q[i];
    		dft(a, n, 1); dft(b, n, 1);
    		ff(i, 0, n) a[i] = a[i] * b[i] % mo;
    		dft(a, n, -1);
    		p.re(n0);
    		ff(i, 0, n0) p[i] = a[i];
    		return p;
    	}
    }
    using ntt :: operator *;
    using ntt :: dft;
    
    V qni(V a) {
    	V b; b.re(1); b[0] = ksm(a[0], mo - 2);
    	for(int n = 1; n < a.si * 2; n *= 2) {
    		V c = a; c.re(n); c.re(2 * n);
    		V d = b; d.re(2 * n);
    		dft(d, 1); dft(c, 1);
    		ff(i, 0, 2 * n) d[i] = d[i] * d[i] % mo * c[i] % mo;
    		dft(d, -1);
    		b.re(n);
    		ff(i, 0, n) b[i] = (b[i] * 2 - d[i] + mo) % mo;
    	}
    	return b;
    }
    
    const int N = 5e5 + 5;
    
    ll fac[N], nf[N];
    int n, a[N];
    
    V p, q;
    
    int main() {
    	ntt :: bd();
    	scanf("%d", &n);
    	fo(i, 1, n) scanf("%d", &a[i]);
    	fac[0] = 1; fo(i, 1, n) fac[i] = fac[i - 1] * i % mo;
    	nf[n] = ksm(fac[n], mo - 2); fd(i, n, 1) nf[i - 1] = nf[i] * i % mo;
    	sort(a + 1, a + n + 1);
    	p.re(n + 1); fo(i, 0, n) p[i] = ksm(2, (ll) i * (i - 1) / 2) * nf[i] % mo;
    	p = qni(p);
    	fo(i, 0, n) p[i] = p[i] * ksm(2, (ll) (n - i) * (n - i - 1) / 2) % mo;
    	q.re(n + 1);
    	fo(i, 0, n) q[i] = nf[i];
    	p = p * q;
    	fo(i, 0, n) p[i] = p[i] * fac[i] % mo;
    	ll ans = 0;
    	fo(i, 1, n) ans = (ans + (ll) a[i] * (p[i - 1] - p[i] + mo)) % mo;
    	pp("%lld
    ", ans);
    }
    
    
  • 相关阅读:
    toString
    React系列,jsx
    React系列,初识
    如何在数据库中存储一棵树
    Linux-gate.so技术细节
    主引导记录MBR/硬盘分区表DPT/主分区、扩展分区和逻辑分区/电脑启动过程
    8259A工作原理描述
    汇编指令大全
    汇编条件判断整理(JCC,CMP/TEST的实现)
    【汇编】字符串处理指令 stosb、lodsb、movsw、scasb、rep
  • 原文地址:https://www.cnblogs.com/coldchair/p/11716345.html
Copyright © 2020-2023  润新知