度度熊为了完成毕业论文,需要收集一些数据来支撑他的论据,于是设计了一份包含 mm 个问题的调查问卷,每个问题只有 'A' 和 'B' 两种选项。
将问卷散发出去之后,度度熊收到了 nn 份互不相同的问卷,在整理结果的时候,他发现可以只保留其中的一部分问题,使得这 nn 份问卷仍然是互不相同的。这里认为两张问卷是不同的,当且仅当存在至少一个被保留的问题在这两份问卷中的回答不同。
现在度度熊想知道,存在多少个问题集合,使得这 nn 份问卷在只保留这个集合的问题之后至少有 kk 对问卷是不同的。
Input
第一行包含一个整数 TT,表示有 TT 组测试数据。
接下来依次描述 TT 组测试数据。对于每组测试数据:
第一行包含三个整数 nn,mm 和 kk,含义同题目描述。
接下来 nn 行,每行包含一个长度为 mm 的只包含 'A' 和 'B' 的字符串,表示这份问卷对每个问题的回答。
保证 1≤T≤1001≤T≤100,1≤n≤1031≤n≤103,1≤m≤101≤m≤10,1≤k≤1061≤k≤106,给定的 nn 份问卷互不相同。
Output
对于每组测试数据,输出一行信息 "Case #x: y"(不含引号),其中 x 表示这是第 xx 组测试数据,y 表示满足条件的问题集合的个数,行末不要有多余空格。
Sample Input
2 2 2 1 AA BB 2 2 2 AA BB
Sample Output
Case #1: 3 Case #2: 0
解题思路:我们将A-B的问题序列转化成为0-1二进制序列从而转为整数特征,而问题子集情况也可以使用0-1进制序列表示。通过位运算就可以表示问题子集对应的问题结果0-1序列,进而转为整数特征。先将n个问卷的问题特征值排序。排序后就可以清晰得到相同问题问卷数目,比如有这样的排序结果[1 2 2 3 3],那么问卷2 和 3的问题是一样的,问卷 4 和 5 的是一样的。通过相同问题数目就可以很容易得到相同问题问卷的对数。我们想如果将总的问题对数减去相同的问题的问卷对数那么就得到了不一样问卷的对数。上面的例子就是5 * 4 / 2 - 2 * 1 / 2 - 2 * 1 / 2 等于8。
#include<stdio.h>
#include<string.h>
#include<string>
#include<iostream>
#include<algorithm>
#include<bitset>
#include<map>
using namespace std;
typedef long long ll;
int n,m,k;
ll ans;
ll a[2010],b[2010];
bool ok(ll x)
{
for(int i=1; i<=n; i++)
{
b[i]=a[i]&x;
}
map<ll,ll>mp;
for(int i=1; i<=n; i++)//二进制整数值相容代表调查问卷相同。
{
mp[b[i]]++;
}
ll total=n*(n-1)/2;
map<ll,ll>::iterator it;
for(it=mp.begin(); it!=mp.end(); it++)
{
if(it->second>1)
{
total=total-it->second*(it->second-1)/2;
}
}
if(total>=k)
return true;
else
return false;
}
int main()
{
int T,t=1;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1; i<=n; i++)
{
char str[2010];
int s[2010];
scanf("%s",str);
bitset<12> bs;
for( ll j = 0; j < strlen(str); j ++ )
{
if( str[j] == 'A' )
{
bs[j]=0;
}
else
{
bs[j] = 1;
}
}
a[i]=bs.to_ulong();
}
ans=0;
for(ll i=1; i<=(1<<m)-1; i++) //将问题的所有子集转化为求每个子集对应的二进制整数
{
if(ok(i))
{
ans++;
}
}
printf("Case #%d: %lld\n",t++,ans);
}
return 0;
}