[递推][数学]数字

题目描述
一个数字被称为好数字当他满足下列条件:

  1. 它有2*n个数位,n是正整数(允许有前导0)

  2. 构成它的每个数字都在给定的数字集合S中。

  3. 它前n位之和与后n位之和相等或者它奇数位之和与偶数位之和相等

例如对于n=2,S={1,2},合法的好数字有1111,1122,1212,1221,2112,2121,2211,2222这样8种。

已知n,求合法的好数字的个数mod 999983。

Input

第一行一个数n。

接下来一个长度不超过10的字符串,表示给定的数字集合。

Output

一行一个数字表示合法的好数字的个数mod 999983。

Sample Input

2
0987654321

Sample Output

1240

Data Constraint

对于20%的数据,n≤7。

对于100%的.据,n≤1000,|S|≤10。

分析
首先我们设递推数组fi,j表示用i个数字加起来能达到j的方案数
预处理:f0,0=1
然后我们可以得出递推式为:
fi,j=∑fi-1,j-a[k](k=1~n并j-a[k]>=0)% 999983
(提示,j最大值为i*ak中最大值,但作者偷懒直接用9。反正都一样啦,至于为什么自己思考一下)
然后,我们可以发现,由于是2n的序列,所以前n位与后n位相等和奇数位与偶数位相等的方案数是一样的,所以乘2即可
方案总数为2*∑fn,i*fn,i(i=0~n*9)

你以为这样就结束了吗
怎么可能啊
我们可以很轻易地发现前n位与后n位相等的时候,有一些情况会和奇数位与偶数位相等的方案重叠
所以我们要减掉一些
减掉什么呢?
我们发现,上面的情况就相当于前n位的奇偶数位相等和后n位的奇偶数位相等
所以式子是:
(接下来l1表示n中的奇数个数,l2表示n中的偶数个数)
(∑fl1,i(i=0~l1*9)∑fl2,i(i=0~l2*9))(∑fl1,i(i=0~l1*9)*∑fl2,i(i=0~l2*9))
然后乘法分配律得
∑fl1,i(i=0~l1*9)²∑fl2,i(i=0~l2*9)²
然后之前的值减去这个时,可能为负数(因为求余,并不是算法特判),所以要加999983后模999983

#include <iostream>
#include <cstdio>
#include <cstring>
#define rep(i,a,b) for (i=a;i<=b;i++)
#define mod 999983
using namespace std;
int n;
char s[11];
int a[11];
long long f[1001][9001];
int i,j,k,l,l1,l2;
long long firans,secans,sumans;
int main()
{
    scanf("%d",&n);
    scanf("%s",s+1);
    l=strlen(s+1);
    rep(i,1,l)
    a[i]=s[i]-48;
    f[0][0]=1;
    rep(i,1,n)
     rep(j,0,i*9)
      rep(k,1,l)
       if (j-a[k]>=0)
        f[i][j]=(f[i][j]+f[i-1][j-a[k]])%mod;
    rep(i,0,n*9)
     sumans=(sumans+2*f[n][i]*f[n][i])%mod;
    l1=(n+1)/2;l2=n/2;
    rep(i,0,l1*9)
     firans=(firans+f[l1][i]*f[l1][i]%mod)%mod;
    rep(i,0,l2*9)
     secans=(secans+f[l2][i]*f[l2][i]%mod)%mod;
    sumans=(sumans-firans*secans%mod+mod)%mod;
    printf("%lld",sumans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值