HDU 6151 Party (二分图)

来源:互联网 发布:php pack 函数 java 编辑:程序博客网 时间:2024/06/05 04:29

Problem

被含糊的题意坑到了,竟然都没想到是原题(变体? CERC 2016 B)

派对预选受邀人可视作二分图,n 个男孩与 m 个女孩分属两个不相交集合。每人都有一个标记 a1,a2,,anb1,b2,,bmn×m 的 01 矩阵,表示二分图的中的边(0 为不存在这条边,1 为存在)。

有 q 个询问,每个询问给定一个 g ,问在所有标号为 g 的倍数的点组成的子图中,有多少中取点方式,使得选择的点至少是一条边的某个端点,同时不存在任意两条边共享一个端点。

Idea

霍尔定理:二分图 G 中的两不相交点集 X, Y ,X={X1, X2, X3, …, Xn} , Y = {Y1, Y2, Y3, …, Ym} ,G 中有一组无公共点的边,一端恰好为组成 X 的点的充要条件为: X 中的任意 k 个点至少与 Y 中的 k 个点相邻。

记录输入的所有信息,mask_right 表示与右部点 i 相邻的所有左部点的状压状态, mask_left 类似。

问题可以视作为:

ansLeft = 有多少个不同的左部点集合 X ,使得 X 中的任意 k 个点至少与右部中 k 个点相邻。

ansRight = 有多少个不同的右部点集合 Y ,使得 Y 中的任意 k 个点至少与左部中 k 个点相邻。

ans = ansLeft * ansRight - 1 (ansLeft 和 ansRight 中都包含空集,答案不允许空集)。

Code

// 部分代码优化来自于 CERC-2016 B 的官方标程// 虽然还是在 HDOJ 上跑了 1248 ms .#include<bits/stdc++.h>using namespace std;const int N = 20, M = 20;int T, n, m, q, g;bool A[N], B[M];int a[N], b[M];char grid[N][M];int mask_right[M];int mask_left[N];int solve(int* mask, int n, int m, bool *A, bool *B) {    static int can[1<<N];    int ans = 0;    int STD = 0;    for(int i=0;i<n;i++)        if(A[i] == true)    STD |= (1<<i);    can[0] = true;    for(int state=0;state<(1<<n);state++)    {        if((STD | state) != STD)    continue;        int pcnt = 0;        can[state] = true;        for(int i=0;i<n;i++)            if(A[i] && (state & (1<<i)))    can[state] &= can[state-(1<<i)];        for(int i=0;i<n;i++)            if(A[i] && (state & (1<<i)))                ++pcnt;        int c = 0;        for(int j=0;j<m;j++)            if(B[j] && (mask[j] & state))   ++c;        if(c < pcnt)    can[state] = false;        if(can[state])  ans++;    }    return ans;}   int main(){    scanf("%d", &T);    for(int ica=1;ica<=T && scanf("%d %d %d", &n, &m, &q)!=EOF;ica++)    {        memset(mask_left, 0, sizeof(mask_left));        memset(mask_right, 0, sizeof(mask_right));        for(int i=0;i<n;i++)        for(int j=0;j<m;j++)        {            scanf(" %c", &grid[i][j]);            grid[i][j] -= '0';            if(grid[i][j])                  mask_right[j] |= 1<<i,                mask_left[i] |= 1<<j;        }        for(int i=0;i<n;i++)            scanf("%d", &a[i]);        for(int j=0;j<m;j++)            scanf("%d", &b[j]);        printf("Case #%d: ", ica);        while(q-- && scanf("%d", &g))        {            for(int i=0;i<n;i++)                if(a[i] % g == 0)   A[i] = 1;                else    A[i] = 0;            for(int j=0;j<m;j++)                if(b[j] % g == 0)   B[j] = 1;                else    B[j] = 0;            long long ansLeft = solve(mask_right, n, m, A, B);            long long ansRight = solve(mask_left, m, n, B, A);            printf("%lld%c", ansLeft * ansRight - 1, q?' ':'\n');        }    }}