题目描述
在某个夜黑月高的晚上,!!!,原谅我编不下去了。
很美吧?放松之后,继续做题吧。
HS(Handsome)的Ocean在纸上写下NN个整数,Ocean把它定义为OO序列。
Ocean认为一个序列的价值的是:序列中不同元素个数。
现在他想知道OO序列中所有子序列的价值之和。
比如说:序列(1,1,2,2)(1,1,2,2)价值为22,因为序列中有11和22两个不同元素。
比如序列(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<=10000,1<=N<=50,1<=ai<=10。1<=T<=10000,1<=N<=50,1<=ai<=10。
输出
对每组测试数据,输出一个结果代表所有子序列价值之和。由于结果会很大,请用longlonglonglong(%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
来源
CZY
并不会写 , 是参考了别人的代码写出来的,组合数学太弱==。
思路: 设想 , 我求出不同数字= i 组成的集合的个数 f[i]( f[i]代表 i个不同的数组成的长度为 [1,n] 的集合的个数) , 然后答案不就是每个f[i]乘以i的和吗? 也就是说 ans = f[1]1+ f[2】 2 + f[3]*3 + … + f[n]*n 喽。 根据f[i]的定义很容易写出转移方程 f[i] = f[i-1] 乘以(sum[1….n]) , sum[i] = pow(2,i) - 1(也就是个数为i的集合的子集合数)。
代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
typedef long long ll;
int main()
{
int b[12],t; ll f[100];
scanf("%d",&t);
while(t--)
{
int n,i,j,a;
memset(f,0,sizeof(f));
memset(b,0,sizeof(b));
scanf("%d",&n);
for(i=1; i<=n; i++)
{
scanf("%d",&a);
b[a]++;
}
int num = 0;
for(i=1; i<=10; i++)
{
if(b[i] != 0)
{
num++;
ll t = (ll)pow(2,b[i]) - 1; //它本身的组合数
//如果正序的话,会造成诸如 5加到 c[1] 被c[1]加过的5又加给 c[2] 了 , 这就用多了是不?
for(j=num; j>=1; j--)
{
if(j == 1)
f[j] += t;
else
f[j] += f[j-1] * t;
}
}
}
ll ans = 0;
for(i=1; i<=num; i++)
ans += i * f[i];
printf("%lld\n",ans);
}
return 0;
}