HDU 5713 K个联通块 状压dp枚举子集 (2016百度之星复赛)

原创 2016年05月31日 22:15:34

题意


众所周知,度度熊喜欢图,尤其是联通的图。

今天,它在图上又玩出了新花样,新高度。有一张无重边的无向图, 求有多少个边集,使得删掉边集里的边后,图里恰好有K个连通块。 

推荐题目:Codeforces #332 div 2 E Nuts (599E) 状压+树形+计数


#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
#include <iostream>
#include <map>
#define pii pair<int,int>
#define mp(a,b) make_pair<a,b>
#define xx first
#define yy second
#define mem(a) memset( a, 0, sizeof(a) )
using namespace std;
typedef long long ll;

ll dp[20][1<<15], f[1<<15], g[1<<15], f2[1000];
int fa[20], vis[1<<15];
int w[1<<15], n, m, k;
const ll mod = 1e9+9;

/*
dp[i][st]存的是现在有i个连通块,加入的点的情况是st
f[st]存的是这个连通块有多少个作为连通块的形态数(就是这个连通块包含的边怎么个删法使他还是个连通块,有多少种情况)
g[st]辅助数组
抓住  第一个点一定属于一个连通分量(所以通俗来说枚举子集时只需要枚举一半即可)

*/
int root( int x )
{
    if( fa[x] == x ) return x;
    else return root( fa[x] );
}

void merge( int a, int b )
{
    if( root(a) == root(b) ) return ;
    int pa, pb;
    pa = root(a);
    pb = root(b);
    fa[pa] = pb;
    return ;
}

int lowbit( int x )
{
   return x&-x;
}


void solve()
{
    int i, j, st;
    cin >> n >> m >> k;
    mem(f);mem(g);mem(vis);
    for( i = 0; i < n; i ++ )fa[i] = i;
    for( i = 1; i <= m; i ++ ){
        int x, y;
        scanf("%d %d", &x, &y);
        x --; y -- ;
        for( j = 0; j < (1<<n); j ++ ){
            if( ( j&(1<<x) ) && ( j&(1<<y) ) ){
                f[j] ++;
            }
        }
        merge( x, y );
    }// 用并查集判连通
    for( st = 1; st < (1<<n); st ++ ){
        int p = lowbit(st), to = -1, fail = 0;
        for( i = 0; i < n; i ++ ){
            if( st & ( 1 << i ) ){
                if( to == -1 ){
                    to = root(i);
                }
                else{
                    if( root(i) != to ){
                        fail = 1;
                        break;
                    }
                }
            }
        }
        if( fail ){
            continue;
        }
        else vis[st] = 1;
        f[st] = f2[f[st]];
        g[st] = f[st];
        for( j = st; j ; j = (j-1) & st ){
            if( j == st ) continue;
            if( !vis[j] ) continue;
            if( j & p ){
                f[st] -= ((ll)f[j]*g[j^st])%mod;
                f[st] %= mod;
            }
        }
    }// vis数组判这个子集本身是否连通,不联通f g = 0(可以不用判断)
    memset( dp, 0, sizeof(dp) );
    dp[0][0] = 1;
    for( i = 1; i <= k; i ++ ){
        for( st = 1; st < (1<<n); st ++ ){
            int p = lowbit(st), sub;
            for( sub = st; sub; sub = ( sub - 1 )&st ){
                if( !vis[sub] ) continue;
                if( p & sub ){
                    dp[i][st] += ( (ll)dp[i-1][sub^st]*f[sub] )%mod;
                    dp[i][st] %= mod;
                }
            }
        }
    }
    ll ans = (dp[k][(1<<n)-1]+mod)%mod;
    printf("%lld\n", ans);
}

void init()
{
    f2[0] = 1;
    for( ll i = 1; i <= 999; i ++ ){
        f2[i] = f2[i-1]*2%mod;
    }
}

int main()
{
    int cas = 1, T;
    init();
    cin >> T;
    while( T -- ){
        printf("Case #%d:\n", cas ++);
        solve();
    }
}


版权声明:本文为博主原创文章,未经博主允许不得转载。

hdu 5713 K个联通块 2016百度之星复赛1002 DP

用dp[i][j] 表示 状态i分成k个联通块的方案数。状态i的二进制第j位代表编号为j的点。 f[i]表示状态i内删边,仍保持一个联通块的方案数。 则有 转移方程 dp[i][k] = sum(dp...
  • Techmonster
  • Techmonster
  • 2016年05月30日 17:52
  • 1156

HDU 5977 树的点分治 + 状态压缩 + 枚举子集

题意: 给一棵节点数为n,节点种类为k的无根树,问其中有多少种不同的简单路径,可以满足路径上经过所有k种类型的点?(a->b与b->a算作两条路径,起点与终点也可以相同) 思路: 现场赛的时候k的大...
  • Bahuia
  • Bahuia
  • 2016年11月07日 20:09
  • 1186

动态规划之状态压缩dp入门

状态压缩动态规划(简称状压dp)是另一类非常典型的动态规划,通常使用在NP问题的小规模求解中,虽然是指数级别的复杂度,但速度比搜索快,其思想非常值得借鉴。 为了更好的理解状压dp,首先介绍位运算相关的...
  • u011077606
  • u011077606
  • 2015年02月04日 15:54
  • 12236

HDU 5713 & 2016"百度之星" 复赛(Astar Round3)1002 k个联通块

题意:众所周知,度度熊喜欢图,尤其是联通的图。 今天,它在图上又玩出了新花样,新高度。有一张无重边的无向图, 求有多少个边集,使得删掉边集里的边后,图里恰好有K个连通块。思路:首先可以很容易想到状态...
  • u014664226
  • u014664226
  • 2016年05月30日 20:20
  • 881

HDU5713 2016"百度之星" - 复赛(Astar Round3)K个联通块

一道状态压缩动态规划题 题目描述:众所周知,度度熊喜欢图,尤其是联通的图。今天,它在图上又玩出了新花样,新高度。有一张无重边的无向图, 求有多少个边集,使得删掉边集里的边后,图里恰好有K个连通块。 ...
  • di4CoveRy
  • di4CoveRy
  • 2016年06月17日 14:08
  • 834

HDU 5713 K个联通块【状压计数dp……补集转化?

显然可以f[s][i] 表示点集s有i个连通块的方案数,枚举子集的时候,令其中一个的i=1,并强行把lowbit(s)表示的节点塞在i=1的子集里面,就避免了算重 然后考虑如何计算对于点集s 全部连...
  • Flaze_
  • Flaze_
  • 2016年12月16日 00:02
  • 256

HDU-5713-K个联通块 状压dp 计数技巧 去重技巧

题意 给一张无重边(可能有自环),求有多少种方案使得删除一些边后有KK个连通块。题解 删边等于添边。设dp[s][i]dp[s][i]表示状态为s的子集有k个连通块的方案数,则有dp[S0][...
  • cosiness
  • cosiness
  • 2016年06月01日 21:17
  • 475

HDU 5714 拍照 优先队列前后扫 (2016百度之星复赛)

题意: 小明在旅游的路上看到了一条美丽的河,河上有许多船只,有的船只向左航行,有的船只向右航行。小明希望拍下这一美丽的风景,并且把尽可能多的船只都完整地拍到一张照片中。 小明位于河的边...
  • tianji_fange_tuhao
  • tianji_fange_tuhao
  • 2016年05月31日 22:34
  • 274

HDU 5823 color II(枚举子集状压dp+染色数)

http://acm.hdu.edu.cn/showproblem.php?pid=5823这题是求一个子图的最少染色数,开头觉得,这不就是搜索么,O(n2)O(n^2)的复杂度,但是后来听说最小染色...
  • Miracle_ma
  • Miracle_ma
  • 2016年08月12日 13:37
  • 384

hdu-5823-color II-状压DP-枚举子集的子集

http://acm.hdu.edu.cn/showproblem.php?pid=5823 给你一个无向图,然后要枚举所有点的子集的最小染色方案数,然后输出 ∑ans[i]*233^i m...
  • viphong
  • viphong
  • 2016年08月15日 23:20
  • 467
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:HDU 5713 K个联通块 状压dp枚举子集 (2016百度之星复赛)
举报原因:
原因补充:

(最多只允许输入30个字)