题目描述
在某个夜黑月高的晚上,!!!,原谅我编不下去了。
很美吧?放松之后,继续做题吧。
HS(Handsome)的Ocean在纸上写下 N N个整数,Ocean把它定义为 O O序列。
Ocean认为一个序列的价值的是:序列中不同元素个数。
现在他想知道 O O序列中所有子序列的价值之和。
比如序列 (1,1,1) (1,1,1),共有 7 7个子序列, (1)、(1)、(1)、(1,1)、(1,1)、(1,1)、(1,1,1)。 (1)、(1)、(1)、(1,1)、(1,1)、(1,1)、(1,1,1)。 价值之和为 7 7。
输入
第一行输入一个整数
T
T,代表有
T
T组测试数据。
每组数据占两行,第一行输入一个整数 N N,代表序列元素个数。
接下来一行输入 N N个整数 ai ai。
注: 1<=T<=10000,1<=N<=50,1<=ai<=10。 1<=T<=10000,1<=N<=50,1<=ai<=10。
每组数据占两行,第一行输入一个整数 N N,代表序列元素个数。
接下来一行输入 N N个整数 ai ai。
注: 1<=T<=10000,1<=N<=50,1<=ai<=10。 1<=T<=10000,1<=N<=50,1<=ai<=10。
输出
对每组测试数据,输出一个结果代表所有子序列价值之和。由于结果会很大,请用
longlong
longlong(%lld)。
样例输入
4
3
1 1 1
4
1 1 1 1
4
10 10 10 8
20
1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
样例输出
7
15
22
7864320
题解:先转态压缩~组合数学~然后统计每个元素的贡献即可~
每个集合(即每个数是否选取)可以用二进制表示~1~代表选取~0~代表不选取~只用 2 ^ 10 即可~~然后统计集合里每个元素的贡献~总贡献 = 每个元素的贡献的乘积~每个元素的子序列 = 2 ^ 元素个数 - 1;时间复杂度O(10 * 2 ^ 10)
对于每个子序列对a[i]都有两种情况: 选或者不选 ,就用10位的二进制表示每个序列的组成 比如1111111100 就说明这个序列由3-10组成 那这个序列的价值就是3+4+5+.....+10 构成这个序列的个数有(2^cn[3]-1)*(2^cnt[4]-1)*(2^cnt[4]-1)*........*(2^cnt[10]-1) 个 cnt[i]表示原序列中出现了多少个i , 3在圆序列中出现了cnt[3]次如果子序列含有3 那么这个序列中的3的个数可能是1,2,3,.....cnt[3]个,就是从cnt[3]中抽出1,2,3,.. cnt[3]个数 用二项式 C(n,1)+C(n,2)+C(n,3)+......+C(n,n)=2^n-1得出上述式子,
每个元素的子序列 = 2 ^ 元素个数 - 1;就是组合数学,存在该元素的不同组合数。
#include<cstdio>
#define LL long long
int main()
{
int t;
int a[13];
scanf("%d",&t);
while(t--)
{
int N, c;
scanf("%d",&N);
for(int i=0;i<=10;i++)
a[i]=0;
for(int i=0;i<N;i++){
scanf("%d",&c);
a[c]++;
}
LL m=1<<10;
LL sum=0;
for(LL i=1;i<m;i++){
LL num=0,ans=1;
for(int j=0;j<10;j++){
if(i&(1<<j)){ //判断该元素是否在集合中
num++; //如果存在该元素,就加1,记录的是存在不同元素的个数
ans*=(((LL)1<<a[j+1])-1); //该元素存在的不同组合数
}
}
sum+=num*ans;
}
printf("%lld\n",sum);
}
return 0;
}
如果一个序列的价值:该序列中不同元素之和,只需将num++改为num+=j。