zoukankan      html  css  js  c++  java
  • Comet Contest#11 F arewell(DAG计数+FWT子集卷积)

    传送门.

    题解:


    4月YY集训时做过DAG计数,和这个基本上是一样的,但是当时好像直接暴力子集卷积,不然我省选时不至于不会,这个就多了个边不选的概率和子集卷积。

    DAG计数是个套路来的,利用的是DAG中入度为0的点。

    (f[S])表示只考虑s里的点的诱导子图形成DAG的方案数。

    枚举一个(T|S~and~T=empty),这个T就是新的图中度数为0的点,首先它们之间要没有边,然后(T)(S)间的边要么没有,要么都由(T->S),记(cnt[S])表示S里的边数,这转移系数是:
    ({1over 3}^{g[T]}*{{2over 3}^{g[S+T]}over {2over 3}^{g[S]+g{T}}})

    注意这样会算重,因为会枚举到度数为0的点的子集,那么容斥系数((-1)^{|T|+1}),考虑用(sum_{i=1}^{|T|}(-1)^{i+1}*C_{|T|}^i=1)来证明。

    直接卷积是(O(3^n)),然后就上or FWT + 1的个数的老套路了,复杂度(O(2^n*n^2))

    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;
    }
    
    const ll w1 = ksm(3, mo - 2), w2 = 2 * ksm(3, mo - 2) % mo;
    const ll nw1 = ksm(w1, mo - 2), nw2 = ksm(w2, mo - 2);
    
    const int N = 21;
    
    const int M = 1 << 20;
    int n, m, x, y, a2[N];
    int bz[N][N];
    ll f[M], nf[M], g[M];
    int cnt[M];
    
    void dft(int *a, int n, int f) {
    	for(int h = 1; h < n; h *= 2) for(int j = 0; j < n; j += 2 * h) ff(i, 0, h) {
    		if(f == 1) a[i + j + h] = (a[i + j + h] + a[i + j]) % mo; else
    			a[i + j + h] = (a[i + j + h] - a[i + j]) % mo;
    	}
    }
    
    int a[21][M], b[21][M];
    
    int main() {
    	scanf("%d %d", &n, &m);
    	a2[0] = 1; fo(i, 1, n) a2[i] = a2[i - 1] * 2;
    	fo(i, 1, m) {
    		scanf("%d %d", &x, &y);
    		x --; y --;
    		bz[x][y] = 1;
    	}
    	ff(s, 1, a2[n]) cnt[s] = cnt[s - (s & -s)] + 1;
    	ff(s, 0, a2[n]) {
    		f[s] = g[s] = nf[s] = 1;
    		if(s == 0) continue;
    		int st;
    		ff(i, 0, n) if(s >> i & 1) { st = i;}
    		f[s] = f[s ^ (1 << st)];
    		g[s] = g[s ^ (1 << st)];
    		nf[s] = nf[s ^ (1 << st)];
    		ff(i, 0, st) if(s >> i & 1) {
    			if(bz[st][i]) f[s] = f[s] * w2 % mo, nf[s] = nf[s] * nw2 % mo, g[s] = g[s] * w1 % mo;
    			if(bz[i][st]) f[s] = f[s] * w2 % mo, nf[s] = nf[s] * nw2 % mo, g[s] = g[s] * w1 % mo;
    		}
    	}
    	fo(i, 1, n) {
    		ff(j, 0, a2[n]) if(cnt[j] == i)
    			b[i][j] = nf[j] * g[j] % mo * ((cnt[j] & 1) ? 1 : -1);
    		dft(b[i], a2[n], 1);
    	}
    	a[0][0] = 1; dft(a[0], a2[n], 1);
    	fo(w, 0, n) {
    		fo(j, 1, n - w) {
    			ff(i, 0, a2[n]) a[j + w][i] = ((ll) a[w][i] * b[j][i] + a[j + w][i]) % mo;
    		}
    	}
    	dft(a[n], a2[n], -1);
    	ll ans =  a[n][a2[n] - 1];
    	ans = (ans % mo  + mo) * f[a2[n] - 1] % mo;
    	pp("%lld
    ", ans);
    } 
    
    
  • 相关阅读:
    Linux 共享库
    使用Visual Studio(VS)开发Qt程序代码提示功能的实现(转)
    ZOJ 3469 Food Delivery(区间DP)
    POJ 2955 Brackets (区间DP)
    HDU 3555 Bomb(数位DP)
    HDU 2089 不要62(数位DP)
    UESTC 1307 windy数(数位DP)
    HDU 4352 XHXJ's LIS(数位DP)
    POJ 3252 Round Numbers(数位DP)
    HDU 2476 String painter (区间DP)
  • 原文地址:https://www.cnblogs.com/coldchair/p/11575289.html
Copyright © 2011-2022 走看看