說在前面
me對計數類題目果然還是很陌生啊…
題目
題目大意
給出一個 個點的有向圖,求出其強連通子圖的個數
範圍:
輸入輸出格式
輸入格式:
第一行兩個整數 ,表示該有向圖的邊數和點數
接下來每行兩個整數 ,表示有一條邊從 到
輸出格式:
輸出答案對 取模的結果
解法
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() ;
}