[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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章