snnu1111(子序列求和)

1111: 子序列求和

Time Limit: 3 Sec  Memory Limit: 64 MB
Submit: 10  Solved: 2
[Submit][Status][Web Board] [Edit]

Description

给出n个数字,分别为1,2,3,……,n。从中选出t个数字,且这t个数字和为x的方案数为ft,x 。给出m。输出下面的值:
 
数据满足n <= 20000,m <= min(10, n)。

Input

第一行输入T表示测试数据的行数。接下来T行,每行两个数字n, m。T <= 20。

Output

对于每组测试数据,输出一行。

Sample Input

1 4 2

Sample Output

64

HINT

 

 

Source

用d[t][x]表示t个数字和为x的方案数,且这t个数字中的最大数字不超过n。
核心思想 : 给d[t][x]中的每个数字减掉一,变成d[t][x-t],d[t][x]应该等于d[t][x-t].
如果这t个数字中的最小数字为1的话,减去1,最小数字就变为0,相当于t-1个数字,d[t-1][x-t]
,然而这两种情况都未考虑,最大数字不超过n的情况,假设d[t][x-t]中最大数字为n,给这t个数字每个加上1,
那么最大数字就为n+1,超过n,所以,要去掉n+1,这个最大数字,减掉这种情况,相当于减掉d[t-1][x-t].
还有一个坑就是A+B-C,由于要取模,所以A+B可能小于C,(A+B-C+MOD)%MOD;
 (d%MOD+MOD)%MOD;
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
#define maxn 21000
#define LL long long
#define MOD  1000000007
int n,m;
LL answer;
LL d[15][maxn*15];
LL f[15];
void init()
{
    memset(d,0,sizeof(d));
    for(int i=1;i<15;i++)
        f[i]=1;
}
LL pow(int a,int b)
{
    LL ans=1;
    for(int i=1;i<=b;i++)
    {
        ans=(ans*a)%MOD;
    }
    return ans;
}
void solve()
{
    for(int i=1;i<=n;i++)
        d[1][i]=1;
        f[1]=pow(2,n);
     // cout<<f[1]<<endl;
     for(int t=2;t<=m;t++)
     {
         for(int x=1;x<(t*n);x++)
        {
            d[t][x]=(d[t][x-t]+d[t-1][x-t]-d[t-1][x-(n+1)] + MOD)%MOD;

            f[t]=(f[t]*(d[t][x]+1))%MOD;
            //printf("%d %d %d\n",t,x,d[t][x]);
        }
        //最小值要么为1,要么不为1,
        //当最小值为1时,最大值可能为n,需要减去d[t-1][x-(n+1)];
        //当最小值不为1,最大值也可能为n,需要减去d[t-1][x-(n+1)];
        //由于这两种情况互斥,所以最多只需要减1次
         //cout<<endl;
       //  while(f[t]<0)
          //cout<<f[t]<<endl;
     }

     answer=0;
     for(int t=1;t<=m;t++)
     {
       //  printf("f %d %d\n",t,f[t]);
          answer=(answer+f[t])%MOD;
          answer=answer%MOD;
     }
    // while(answer<0)
     printf("%lld\n",answer%MOD);
}
int main()
{
   // cout<<(-5)%3<<endl;
    int t;
    scanf("%d",&t);
    while(t--)
    {
       init();
       scanf("%d%d",&n,&m);
       solve();
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/xianbin7/p/4678186.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值