1. 程式人生 > >【清華集訓2014】主旋律

【清華集訓2014】主旋律

【清華集訓2014】主旋律

題目大意

給定一張\(n\)個點\(m\)條邊的無向圖,保證該圖整個圖為一個強聯通分量,保證無重邊自環。
現在需要求出:有多少種刪邊方案,使得刪完邊後,整個圖依舊是一個強聯通分量。
資料範圍:\(n\leq 15 , m\leq n(n-1)\)

題解

容斥題是真的雞賊......,這\(TMD\)是人能想出來的?
希望下次自己再來看這篇題解的時候還能看懂吧......
規定\(E[U,V]\)表示出度在\(U\)中,入度在\(V\)中的所有邊的集合,其中\(U\)\(V\)為點集。
正難則反(雖然反也夠難的)。
我們改求連邊後整張圖不是一個強聯通分量的方案數。
對於一個點集\(S\)

,我們隨便連邊,然後縮點。
那麼縮完點後,形成的一定是一個\(DAG\)
我們列舉縮完點後,構成出度為\(0\)\(scc\)的點是哪些,設這個點集為\(T\)
那麼每一種\(T\)集合都可以與原來的某些連邊方案對應起來。
我們確定完\(T\)後,\(S-T\)中的點先讓它隨便連,然後\(S-T\)連向\(T\)中的邊也可以隨便連。
我們可以暫時得到一個式子:
\[2^{E[S,S]} = \sum_{T\subset S,T \neq null} g_T *2^{E[S-T,S-T]}*2^{E[S-T,T]}\]
\(f_S\)表示連完\(E[S,S]\)中的邊後,\(S\)整張圖不為一個強聯通分量的方案數,即題目所求。
上式中\(g_T\)
表示把\(T\)中的點連成若干強聯通分量的方案數。
顯然,直接\(g_T = \sum_{s_1,s_2...,s_k} \prod_{k} f_s\)是不對的。
因為由於\(E[S-T,S-T]\)中的邊也是隨便連的,所以還有可能會形成其他的出度為\(0\)\(scc\)
所以這裡應該是存在一個容斥係數的,即:
\[g_T = \sum_{s_1,s_2,...,s_k} coef_k*f_{s_1}f_{s_2}...f_{s_k}\]
我們令這個式子為\(g_T\)的定義式。
考慮一下這個容斥係數\(coef\)到底是什麼。
我們設\(r_k\)表示劃分成\(k\)個出度為\(0\)\(scc\)
的方案數應該要被算幾遍,顯然\(r_k=1\)
然後我們來推容斥係數\(coef\)應該設為多少才能夠使得計算出來的結果與\(r_k\)等效。
對於被劃分成\(k\)個出度為\(0\)\(scc\)的方案,
它在劃分為\(k-1\)箇中被算了\(\binom{k}{k-1}\)次,在劃分為\(k-2\)箇中被算了\(\binom{k}{k-2}\)次......
所以我們有:
\[r_k = \sum_{j=1}^k\binom{k}{j} coef_j\]
顯然是一個二項式反演,結合\(r_k=1\)與二項式定理我們可以得到:
\[coef_k = \sum_{j=1}^k (-1)^{k-j}\binom{k}{j} r_k = ((-1)+1)^k - \binom{k}{0}(-1)^k =(-1)^{k+1}\]
所以我們得到了容斥係數\(coef_k = (-1)^{k+1}\),帶入之前\(g_T\)的定義式中:
\[g_T = \sum_{s_1,s_2,...,s_k} (-1)^{k+1} coef_k*f_{s_1}f_{s_2}...f_{s_k}\]
根據容斥係數可以看出,\(g_{null} = -1\)
現在我們終於弄清楚\(g_T\)究竟應該是什麼了!
到此為止,所有的準備工作已經就緒,下面開始求答案。
回到這個式子:
\[2^{E[S,S]} = \sum_{T\subset S , T\neq null} g_T *2^{E[S-T,S-T]}*2^{E[S-T,T]}\]
注意到這個式子中是沒有\(f\)陣列的,所以:
\[g_S = 2^{E[S,S]} - \sum_{T\subsetneqq S , T\neq null} g_T *2^{S-T,S-T} * 2^{E[S-T,T]}\]
所以順次把\(g\)陣列全部推出來即可。
然後我們來研究一下\(g\)的定義式:
\[g_T = \sum_{s_1,s_2,...,s_k} (-1)^{k+1} coef_k*f_{s_1}f_{s_2}...f_{s_k}\]
注意到,每加入一個\(scc\),容斥係數會乘上一個\(-1\)
由於是無序關係,所以我們可以列舉最小編號所在的強聯通分量\(s_1\),然後就可以得到:
\[g_T = (-1) * \sum_{s_1 \subset T , s_1 \neq null} f_{s_1} g_{T-s_1}\]
類似之前求\(g\)的技巧,移項可以得到:
\[f_T = g_T + \sum_{s_1 \subsetneqq T, s_1 \neq null} f_{s_1} g_{T-s_1}\]
所以說只要解決了\(g\),就可以順次推出\(f\),而\(g\)我們已經會求了,所以就做完了。
累死我了.......
不玩了不玩了,再以不玩這種毒瘤題了......

實現程式碼

#include<bits/stdc++.h>
#define IL inline
#define mod 1000000007
using namespace std ;

int lk[20][20],Inside[(1<<17)],Edge[(1<<17)][20],n,m,pw[233333],pos[(1<<17)] ;
int f[(1<<17)] , g[(1<<17)] , num[(1<<17)] , mx ; 

IL void Pre() {
    for(int i = 1,u,v; i <= m; i ++) cin >> u >> v , lk[u][v] ++ ;
    mx = (1 << n) ; 
    for(int S = 0; S < mx; S ++)
        for(int i = 1; i <= n; i ++)
            for(int j = 1; j <= n; j ++)
                if((S&(1<<(i-1))) && (S&(1<<(j-1)))) Inside[S] += lk[i][j] ;
    for(int S = 0; S < mx; S ++)
        for(int v = 1; v <= n; v ++) if(S & (1 << (v - 1))) continue ;
            else for(int u = 1; u <= n; u ++)
                     if(S & (1 << (u - 1))) Edge[S][v] += lk[u][v] ;
    for(int S = 0; S < mx; S ++) {
        for(int v = 1; v <= n; v ++) if(S & (1 << (v - 1))) pos[S] = v ;
    }
    pw[0] = 1 ;
    for(int i = 1; i <= m; i ++) pw[i] = (pw[i - 1] << 1) % mod ; return ; 
}

IL void add(int &x , int y) {x += y ; if(x >= mod) x -= mod ; }

int main() {
    freopen("scon.in","r",stdin) ;
    freopen("scon.out","w",stdout) ;
    cin >> n >> m ;
    Pre() ; 
    g[0] = mod - 1 ;
    for(int S = 1; S < mx; S ++) {
        g[S] = pw[Inside[S]] ;
        for(int T = S,tmp; T ; T = (T - 1) & S) {
            num[T] = 0 ; tmp = T ; while(tmp) add(num[T] , Edge[S ^ T][pos[tmp]]) , tmp -= (1 << (pos[tmp] - 1)) ;
        }
        for(int T = S; T ; T = (T - 1) & S) 
            if(S ^ T) add(g[S] , mod - 1ll * g[T] * pw[Inside[S ^ T]] % mod * pw[num[T]] % mod) ;
    }
    for(int S = 1; S < mx; S ++) {
        for(int s1 = S; s1 ; s1 = (s1 - 1) & S) {
            if(s1 & (1 << (pos[S] - 1))) add(f[S] , 1ll * f[s1] * g[S ^ s1] % mod) ; 
        }
        add(f[S] , g[S]) ; 
    }
    cout << f[mx - 1] << endl ; return 0 ; 
}