/** 题目:UVALive 7721 K - 2-ME Set 链接:https://vjudge.net/problem/UVALive-7721 题意:给定n个数,从中取出一个集合,至少包含两个元素,如果集合内任意两个元素取位与都是0,那么是合法集合。 如果5个数为{5,2,2,1,4}那么有9种。(5, 2), (5, 2), (2, 1), (2, 1, 4), (2, 4), (2, 1), (2, 1, 4), (2, 4), and (1, 4). 问最多有多少种合法集合。 思路:定义dp[i]表示构成集合i的方法数。注意这里的i不是状态压缩那种i,就是纯粹集合内所有的数都满足任意取位与为0,所有数位或起来的。 因为每个数大小最多为20000,从1到20000取位或发现最大为32767。所以dp数组大小为32770. dp[i] += dp[j];(j<i,(j&(i-j))==0) 逆序枚举j。 为了优化时间,相同的数自身和自身不可能在同一个集合,所以统一处理。 然后最终计算结果减去所有的单位集合,即每一个数作为一个集合的情况。 */ #include<iostream> #include<cstdio> #include<algorithm> #include<map> #include<vector> #include<queue> #include<cstring> #include<cmath> using namespace std; typedef pair<int,int> P; typedef long long LL; const int N = 2e4+10; const int M = 15; const int mod = 1e9+7; const int INF = 0x3f3f3f3f; int a[N], dp[32770], s[N], num[N]; struct node { int value, cnt; }t[N]; vector<node> v; int main() { int T, cas = 1, n; cin>>T; while(T--) { scanf("%d",&n); memset(num, 0, sizeof num); for(int i = 1; i <= n; i++){ scanf("%d",&a[i]); num[a[i]]++; } int m = n; int n = 0; for(int i = 1; i <= 20000; i++){ if(num[i]){ t[n].cnt = num[i]; t[n].value = i; n++; } } memset(dp, 0, sizeof dp); dp[0] = 1; //v.clear(); //v.push_back(node{0,1}); for(int i = 0; i < n; i++){ if(i==0) s[i] = t[i].value; else s[i] = s[i-1]|t[i].value; } for(int i = 0; i < n; i++){ for(int j = s[i]; j >= t[i].value; j--){ if((t[i].value&(j-t[i].value))==0) dp[j] = (dp[j]+(LL)dp[j-t[i].value]*t[i].cnt%mod)%mod; } } int ans = 0; for(int i = 1; i <= s[n-1]; i++) ans = (ans+dp[i])%mod; printf("Case #%d: %d ",cas++,(ans-m+mod)%mod); } return 0; }