Problem
被含糊的题意坑到了,竟然都没想到是原题(变体? CERC 2016 B)
派对预选受邀人可视作二分图,n 个男孩与 m 个女孩分属两个不相交集合。每人都有一个标记 a1,a2,⋯,an 和 b1,b2,⋯,bm 。 n×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');
}
}
}