[BZOJ3812]-主旋律-子集dp+容斥計數

說在前面

me對計數類題目果然還是很陌生啊…


題目

BZOJ3812傳送門

題目大意

給出一個 n 個點的有向圖,求出其強連通子圖的個數
範圍:n15

輸入輸出格式

輸入格式:
第一行兩個整數 n,m ,表示該有向圖的邊數和點數
接下來每行兩個整數 u,v ,表示有一條邊從 uv

輸出格式:
輸出答案對 109+7 取模的結果


解法

me覺得me還是不寫解法的好,因爲me並沒有get到這道題想讓我們知道什麼…只知道做法emmmm。
不過做法仍然是很妙的,dp計數的時候尋找關鍵點來防止重複統計,然後容斥得答案
Miskcoo的博客傳送門


下面是代碼

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

const int P = 1e9 + 7 ;
int N , M , ein[1<<15] , eout[1<<15] , bi[25] , popcnt[1<<15] ;
long long f[1<<15] , g[1<<15] , h[1<<15] , p[1<<15] , mi2[500] ;

void init(){
    for( int i = 1 ; i <= N ; i ++ ) bi[i] = 1 << ( i - 1 ) ;
    for( int s = 1 ; s < ( 1 << N ) ; s ++ )
        popcnt[s] = popcnt[ s^(s&-s) ] + 1 ;
    mi2[0] = 1 ;
    for( int i = 1 ; i <= N * N ; i ++ )
        mi2[i] = mi2[i-1] * 2 %P ;
}

void solve(){
    for( int s = 1 ; s < ( 1 << N ) ; s ++ ){
        int spe = ( s & -s ) , fs = s ^ spe ;
        for( int i = fs ; i ; i = ( i - 1 ) & fs )
            g[s] = ( g[s] - g[i] * f[s^i] )%P ;
        h[s] = h[fs] + popcnt[ ein[spe]&fs ] + popcnt[ eout[spe]&fs ] ;
        f[s] = mi2[ h[s] ] ;
        for( int i = s ; i ; i = ( i - 1 ) & s ){
            if( i != s ){
                int t = ( s ^ i ) & -( s ^ i ) ;
                p[i] = p[i^t] + popcnt[ eout[t]&i ] - popcnt[ ein[t]&(s^i^t) ] ;
            } else p[i] = 0 ; // p[i] is : edge {s-i} to i ;
            f[s] = ( f[s] - g[i] * mi2[ h[s^i] + p[i] ] )%P ;
        } g[s] = ( g[s] + f[s] ) %P ;
    } printf( "%lld\n" , ( f[ mi2[N]-1 ] + P )%P ) ;
}

int main(){
    scanf( "%d%d" , &N , &M ) , init() ;
    for( int i = 1 , u , v ; i <= M ; i ++ ){
        scanf( "%d%d" , &u , &v ) ;
        ein[ bi[v] ] |= mi2[u-1] , eout[ bi[u] ] |= mi2[v-1] ;
    } solve() ;
}
發佈了268 篇原創文章 · 獲贊 29 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章