HDU 5765 Bonds

比赛时候想了好久,不会。看了官方题解才会......

Bond是极小割边集合,去掉一个Bond之后,只会将原图分成两个连通块。

假设某些点构成的集合为 s(点集中的点进行状压后得到的一个十进制数),那么剩下的点构成的集合为 t=(1<<n)-1-s

如果s是连通的,t也是连通的,那么跨越s、t集合的边的答案就+1(即跨越s、t集合的边构成一个Bond)。 

因此,只需枚举 s 即可。

接下来问题转变成了以下两个问题:

1.如何判断某个点集合是否连通:状压DP预处理。

2.如何让跨越s、t集合的边的答案+1:如果每次遍历所有的边去加答案,时间复杂度爆炸O(m*(1<<n)),因此需要换一种思路:

我们可以计算出所有Bond有几个,然后减去(u,v)不跨越s,t的Bond个数就是(u,v)这条边的答案。

具体看代码吧~~,再贴上官方题解:

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<iostream>
using namespace std;
typedef long long LL;
const double pi=acos(-1.0),eps=1e-8;
void File()
{
    freopen("D:\\in.txt","r",stdin);
    freopen("D:\\out.txt","w",stdout);
}
inline int read()
{
    char c = getchar();  while(!isdigit(c)) c = getchar();
    int x = 0;
    while(isdigit(c)) { x = x * 10 + c - '0'; c = getchar(); }
    return x;
}

int T,n,m,e[25],sum[(1<<20)+10],sz;
int u[500],v[500];
bool f[(1<<20)+10];

void pre()
{
    for(int i=0;i<n;i++) f[1<<i]=1;
    for(int i=0;i<(1<<n);i++)
    {
        if(!f[i]) continue;
        int st=0; for(int j=0;j<n;j++) if(i&(1<<j)) st=st|e[j];
        for(int j=0;j<n;j++)
        {
            if(i&(1<<j)) continue;
            if(st&(1<<j)) f[i|(1<<j)]=1;
        }
    }
}

int main()
{
    scanf("%d",&T); int cas=1;
    while(T--)
    {
        scanf("%d%d",&n,&m);
        memset(e,sz=0,sizeof e); memset(f,0,sizeof f); memset(sum,0,sizeof sum);
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",&u[i],&v[i]);
            e[u[i]]=e[u[i]]|(1<<v[i]), e[v[i]]=e[v[i]]|(1<<u[i]);
        }
        pre();
        for(int i=0;i<(1<<n);i++)
        {
            if(f[i]==0||f[(1<<n)-1-i]==0) continue;
            if(i>(1<<n)-1-i) break;
            sum[i]=1; sum[(1<<n)-1-i]=1; sz++;
        }
        for(int i=0;i<n;i++)
        {
            for(int j=(1<<n)-1;j>=0;j--)
            {
                if((1<<i)&j) continue;
                sum[j]=sum[j]+sum[(1<<i)|j];
            }
        }
        printf("Case #%d:",cas++);
        for(int i=0;i<m;i++) printf(" %d",sz-sum[(1<<u[i])|(1<<v[i])]);
        printf("\n");
    }
    return 0;
}

 

posted @ 2016-07-31 16:56  Fighting_Heart  阅读(478)  评论(0编辑  收藏  举报