JZOJ 3056. 【NOIP2012模拟10.27】数字

一道关于好数字的问题,好数字需满足特定条件,如长度为2*n,数字在给定集合S中,前n位和后n位和相等或奇偶位和相等。使用动态规划(DP)求解,设f[i][j]表示长度为i,数字和为j的方案数。通过计算两种情况的组合并减去重复部分,得到答案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

训练时突然发现了这么一道题:

一个数字被称为好数字当他满足下列条件:
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。

一道很明显的DP。虽然楼主一开始就设错了方程式……
我们设f[i][j]为构造的,长度为i且序列所有数字和为j的方案数。
不难得:f[i][j]=sigma(i=1~N,j=0~i*Max_Num,k=1~S) f[i-1][j-num[k]]
当我们重铸序列后,得出的方案数就能算答案了。

考虑第一种情况:(要求前n为与后n为和相同)因为序列是2*n的,所以只要将两串 和相等且均为n的序列拼在一起就能符合要求。
Ans+=sigma(i=0~N*Max_Num) f[n][i]^2

考虑第二种情况:(要求奇偶数位上的数和相等)同样是有2个n序列构成的,所以对答案的贡献与第一种相同。
Ans+=sigma(i=0~N*Max_Num) f[n][i]^2

但是方案一和二是有重复情况的!

我们可以尝试构造一个序列使他既符合一又符合二。一是两边相等,二是奇偶相等。不妨设左边的奇数为s1,偶数为s2,那么右边的奇数即为s2,偶数为s1,可以枚举s1和s2,算出重复的方案数。
Ans-=sigma(i=0~|(N+1)/2|*Max_Num,j=0~|N/2|*Max_Num) f[|(N+1)/2|][i]^2*f|[N/2|][j]^2

注:|X|为floor(X)和trunc(X).

Code From Zhongjunquan

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 999983
using namespace std; 

int f[1001][9001],tot,n,num[11];

int sqr(int n)
{
    long long t=(long long)n;
    int p=int((t*t)%maxn);
    return p;
}

int main()
{
    scanf("%d\n",&n);
    char ch=getchar();
    while(ch>='0' && ch<='9')
    {
        num[++num[0]]=ch-48;
        ch=getchar();
    }
    f[0][0]=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=i*9;j++)
        {
            tot=0;
            for(int k=1;k<=num[0];k++)
                if(j>=num[k])tot=(tot+f[i-1][j-num[k]])%maxn;
            f[i][j]=tot;
        }
    }
    int a,b,c,odn,nodn;
    a=b=c=0;
    for(int i=0;i<=n*9;i++)a=(a+sqr(f[n][i]))%maxn;
    odn=(n+1)/2;nodn=n-odn;
    for(int i=0;i<=odn*9;i++) b=(b+sqr(f[odn][i]))%maxn;
    for(int i=0;i<=nodn*9;i++) c=(c+sqr(f[nodn][i]))%maxn;
    long long t=(long long)b*(long long)c;
    printf("%d",(a+a-int(t%maxn)+maxn)%maxn);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值