fjnu2013校赛E(数位dp,排列组合)

题意:

给出10个数字,分别表示数i有a[i]个,问这些数都要用上能构成被11整除的数的个数。

题解:

初始状态dp[i][j][x][y] 用前i个数,j位奇数位,奇数位和为x,偶数位和为y满足条件的个数。很明这个内存不允许,其实可以把偶数为的状态删掉,因为偶数为的个数可以用总位数-奇数位的,只要没次记录总位数。变成dp[i][j][x]但是还是太大,用滚动数组dp[2][j][x],然后dp完,枚举奇数位的和得到答案。

这是钟神把BNU 11993 改编的,题目挺好的。

#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
const int oo=0x3f3f3f3f;
const ll OO=1LL<<61;
const ll MOD=10007;
const int maxn=105;
const int maxm=1005;
int num[10];
ll dp[2][105][1005];
ll C[1005][1005];

void Init()
{
    for(int i=0;i<=1000;i++)
    {
        C[i][0]=C[i][i]=1;
        for(int j=1;j<i;j++)
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%MOD;
    }
}

int main()
{
    Init();
    int T,sum,cnt;
    scanf("%d",&T);
    for(int cas=1;cas<=T;cas++)
    {
        sum=cnt=0;
        for(int i=0;i<=9;i++)
        {
            scanf("%d",&num[i]);
            cnt+=num[i];
            sum+=num[i]*i;
        }
        memset(dp,0,sizeof dp);
        dp[0][0][0]=1;
        int now=0,pre=1;
        int tol=0;
        ll temp;
        for(int dig=9;dig>=0;dig--)
        {
            now^=1;
            pre^=1;
            tol+=num[dig];
            memset(dp[now],0,sizeof dp[now]);
            for(int i=0;i<=100;i++)
            {
                for(int j=0;j<=1000;j++)
                if(dp[pre][i][j])
                {
                    for(int a=0;a<=num[dig];a++)
                    {
                        temp=dp[pre][i][j];
                        int da=i+a;
                        int b=num[dig]-a;
                        int db=tol-da;
                        if(dig==0)
                        {
                            if(i==0)
                                temp=0;
                            else
                                temp=(temp*C[da-1][a])%MOD;
                            temp=(temp*C[db][b])%MOD;
                        }
                        else
                        {
                            temp=(temp*C[da][a])%MOD;
                            temp=(temp*C[db][b])%MOD;
                        }
                        dp[now][i+a][j+dig*a]=(dp[now][i+a][j+dig*a]+temp)%MOD;
                    }
                }
            }
        }
        ll ans=0;
        int dig=(cnt-cnt/2);
        for(int i=0;i<=sum;i++)
        {
            int j=sum-i;
            int t=abs(i-j);
            if(t%11==0)
                ans=(ans+dp[now][dig][i])%MOD;
        }
        printf("Case %d: ",cas);
        cout<<ans<<endl;
    }
    return 0;
}
/***

*/








  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值