洛谷 数字 解题报告

数字

题目描述

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

  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。

输入输出格式

输入格式:

第一行一个数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 <cstdio>
#include <cstring>
#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

posted @ 2018-06-26 18:58  露迭月  阅读(365)  评论(0编辑  收藏  举报