数字
题目描述
一个数字被称为好数字当他满足下列条件:
它有2*n个数位,n是正整数(允许有前导0)。
构成它的每个数字都在给定的数字集合S中。
它前n位之和与后n位之和相等或者它奇数位之和与偶数位之和相等
例如对于n=2,S={1,2},合法的好数字有 1111、1122,1212,1221,2112,2121,2211,2222这样8种。
已知n,求合法的好数字的个数mod 999983。
输入输出格式
输入格式:
第一行一个数n。
接下来一个长度不超过10的字符串,表示给定的数字集合。
输出格式:
一行一个数字表示合法的好数字的个数mod 999983。
说明
对于20%的数据,n≤7。
对于100%的.据,n≤1000,|S|≤10。
首先,用DP分别求出满足“前n位之和与后n位之和相等”和“奇数位之和与偶数位之和相等”的,再用容斥原理把它们的交集减去。
第一步很好求,我们发现每个数的位置并不重要,重要的是它的规模\(n\)
用\(dp[i][j]\)代表规模为\(i\)的数的和为\(j\)的时候一共有多少种方案数构成
则第一步的答案为\((\sum_{i=0}^{mx*n} dp[n][i]^2)*2\)。\(mx\)为给定数字集合中最大的数
当两个条件同时满足时,可以如下的充分描述:
前一块中的奇数和等于后一块的偶数和,前一块的偶数和等于后一块的奇数和
为什么要这么描述?因为我们要保证划分出来的子集是不相交的,如果划分出来的子集有包含关系,岂不是求不了嘛
因为\(n\)可能是奇数,所以令\(k_1\)为奇数块规模,\(k_2\)为偶数块规模,我们发现这和之前的问题是一样的
则对“前一块中的奇数和等于后一块的偶数和”这一步的答案为\(\sum_{i=0}^{mx*max(k1,k2)} dp[k1][i]*dp[k2][i]\)
对“前一块的偶数和等于后一块的奇数和”的答案同样是上一行。
把两个答案乘起来即为之前两块的交集
code:
#include
#include
#define ll long long
ll max(ll x,ll y){return x>y?x:y;}
const int N=1001;
const int mod=999983;
ll dp[N][N*10],a[12],mx,ans;
int n,len;
char c[12];
void DP()
{
dp[0][0]=1;
for(int i=1;i<=n;i++)
{
int r=mx*i;
for(int j=0;j<=r;j++)
for(int k=1;k<=len;k++)
if(j>=a[k])
dp[i][j]=(dp[i][j]+dp[i-1][j-a[k]])%mod;
}
}
void cal()
{
for(int i=0;i<=mx*n;i++)
ans=(ans+dp[n][i]*dp[n][i]%mod)%mod;
ans<<=1;
int k1=n>>1,k2=n+1>>1;
ll a0=0,a1=0;
for(int i=0;i<=k1*mx;i++)
a0=(a0+dp[k1][i]*dp[k1][i]%mod)%mod;
for(int i=0;i<=k2*mx;i++)
a1=(a1+dp[k2][i]*dp[k2][i]%mod)%mod;
ans=(ans+mod-a0*a1%mod)%mod;
}
int main()
{
scanf("%d",&n);
scanf("%s",c);
len=strlen(c);
for(int i=1;i<=len;i++)
{
a[i+1]=c[i]-'0';
mx=max(mx,a[i+1]);
}
DP();
cal();
printf("%lld\n",ans);
return 0;
}
2018.6.26