题目描述
一个数字被称为好数字当他满足下列条件:
它有2*n个数位,n是正整数(允许有前导0)
构成它的每个数字都在给定的数字集合S中。
它前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);
}