HPU 1192 Sequence(状态压缩+组合数学)

来源:互联网 发布:mt4软件使用说明 编辑:程序博客网 时间:2024/06/08 19:50

题目描述

在某个夜黑高的晚上,!!!,原谅我编不下去了


很美吧?放松之后,继续做题吧。

HS(Handsome)的Ocean在纸上写下NN个整数,Ocean把它定义为OO序列。

Ocean认为一个序列的价值的是:序列中不同元素个数。

现在他想知道OO序列中所有子序列的价值之和。


比如说:序列(1,1,2,2)(1,1,2,2)价值为22,因为序列中有1122两个不同元素。
比如序列(1,1,1)(1,1,1),共有77个子序列,(1)(1)(1)(1,1)(1,1)(1,1)(1,1,1)(1)、(1)、(1)、(1,1)、(1,1)、(1,1)、(1,1,1)。价值之和为77

输入

第一行输入一个整数TT,代表有TT组测试数据。
每组数据占两行,第一行输入一个整数NN,代表序列元素个数。
接下来一行输入NN个整数aiai

注:1<=T<=100001<=N<=501<=ai<=101<=T<=10000,1<=N<=50,1<=ai<=10。

输出

对每组测试数据,输出一个结果代表所有子序列价值之和。由于结果会很大,请用longlonglonglong(%lld)。

样例输入

431 1 141 1 1 1410 10 10 8201 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10

样例输出

715227864320

题解:先转态压缩~组合数学~然后统计每个元素的贡献即可~

每个集合(即每个数是否选取)可以用二进制表示~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。
0 0