【HDU 5765】Bonds(进制运算妙用)

【HDU 5765】Bonds(进制运算妙用)

Bonds

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 309    Accepted Submission(s): 140

Problem Description
Given an undirected connected graph with N points and M edges. ?? wants to know the number of occurrence in all bonds of graph for every edge.The index of points starts from 0 .
An edge cut E of a Graph G is a set of edges of G and the G would be disconnected after deleting all the edges of E.
A bond of a graph is an edge cut does not have any other edge cut as a proper subset.

Input
The first line of the input gives the number of test cases T ; T test cases follow.
Each test case consists of two integers: N , M, followed by M lines, each line contains two integers u, v , implying an undirected edge between u and v .

limits
T<=20
2<=N<=20
N1<=M<=N(N1)/2
Edges are distinct.
No edge connects to the point itself.
N is larger than 10 in no more than 5 cases.

Output
For each test case output “Case #x: y1 y2 … yN” (without quotes), where x is the test case number (starting from 1), and yi is the occurrence times in all bonds of i-th edge.

Sample Input
2
3 3
0 1
0 2
1 2
3 2
0 1
0 2

Sample Output
Case #1: 2 2 2
Case #2: 1 1

Hint
In first case, {(0,1),(0,2)} , {(0,1),(1,2)} , {(0,2),(1,2)} are bonds.
In second case, {(0,1)},{(0,2)} is bond.

Author
FZU

Source
2016 Multi-University Training Contest 4

题意:
给出一个n个点的无向连通图 G
m条边连通,给出 m 条边u  v

已知极小割边集恰好会将原图分成两块。
询问对于边 0 m1 ,每条边所在的极小割边集的个数

这题用位运算简直不能再精美(从QAQ巨那里学来的

首先题目要找对于每条边 uv 所在的最小割边集的个数。
如果能找出G图中所有的连通图的个数,然后减去包含 uv 边的连通图的个数,就是要找的答案。

接下来只要找出连通图的个数和包含u、v的连通图个数就可以了。
同时避免重复统计。
其他的都在代码(注释)里了~~~

#include <iostream>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <list>
#include <algorithm>
#include <map>
#include <set>
#define LL long long
#define Pr pair<int,int>
#define fread(ch) freopen(ch,"r",stdin)
#define fwrite(ch) freopen(ch,"w",stdout)

using namespace std;
const int INF = 0x3f3f3f3f;
const int msz = 1<<20;
const int mod = 1e9+7;
const double eps = 1e-8;

//Lowbit运算是个好东西,x拆成二进制后,返回低位第一个1所在的进制
//10010->10
//11000->1000
int lb(int x)
{
    return x&(-x);
}

int cnt[msz];
int eg[msz];

//搜索点集S(二进制表示)是否为连通图
bool cal(int S)
{
    //BFS的转换,now表示当前搜索的集合
    int now = lb(S);
    //vis跟vis数组一个意思,表示已经访问过的点集
    int vis = 0;
    int u;

    //当now == vis时,表示已经没有多余的可访问的点了
    //此时now表示的就是集合S中lb(S)所在的联通点集
    while(now != vis)
    {
        //找到当前集合最低位的第一个未被访问的点
        u = lb(now^vis);
        //标记为访问过
        vis |= u;
        //将与该点直接相连且在S中的点加入当前集合
        //eg数组在main中输入边的时候统计了
        now |= eg[u]&S;
    }

    //如果S中每个点都被访问过了 那么S就是连通的
    return vis == S;
}

int u[233],v[233];
int main()
{
    //fread("");
    //fwrite("");

    int t;

    scanf("%d",&t);
    int n,m;

    for(int z = 1; z <= t; ++z)
    {
        scanf("%d%d",&n,&m);

        int tot = (1<<n)-1;

        memset(eg,0,sizeof(eg));
        memset(cnt,0,sizeof(cnt));

        for(int i = 0; i < m; ++i)
        {
            scanf("%d%d",&u[i],&v[i]);
            u[i] = 1<<u[i];
            v[i] = 1<<v[i];
            //eg[u]存放与u相连的点。u直接变为二进制,方便
            eg[u[i]] |= v[i];
            eg[v[i]] |= u[i];
        }

        int ans = 0;

        for(int i = 0; i < tot; ++i)
        {
            //i&1是避免重复统计。
            if((i&1) && cal(i) && cal(tot^i))
            {
                ans++;
                cnt[i]++;
                cnt[tot^i]++;
            }
        }

        //cnt此时表示集合是否连通
        //经过下面的操作 cnt[S]表示S集合所在的连通集的个数
        //i枚举点
        for(int i = 1; i <= tot; i <<= 1)
            for(int j = 0; j <= tot; ++j)
                if(j&i)
                {
                    //将集合S所在的连通图个数赋予S^i(S中除去i点后的集合)
                    cnt[j^i] += cnt[j];
                }

        printf("Case #%d:",z);

        for(int i = 0; i < m; ++i)
        {
            printf(" %d",ans-cnt[u[i]|v[i]]);
        }

        puts("");
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值