说在前面
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() ;
}