hpu 1192 Sequence (dp+组合数学)

题目描述
在某个夜黑月高的晚上,!!!,原谅我编不下去了。

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

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值