[容斥原理][dp]JZOJ P3056 数字

【问题描述】


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


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
 
 
Hint

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


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

 

题解

  • 显然我们可以问题为:
  • 前n位之和与后n位之和相等的方案数+奇数位之和与偶数位之和相等的方案数-前n位之和与后n位之和相等且奇数位之和与偶数位之和相等的方案数

  • 根据容斥原理可以将最后一个转换为:
  • 前n位奇数位之和=后n位偶数位之和 且 前n位偶数位之和=后n位奇数位之和

  • 那么前两个都很容易求,跑一遍dp,f[i][j]表示i个数和为j的可能的方案数
  • 那么i个数可以为n个奇数,也可以为n个偶数
  • 那么前两个的和就是∑(i=1,i<=n*9)2*f[n][i]*f[n][i] (ans)
  • 定义两个数l1=(n+1)/2,l2=n/2
  • l1为前n个数的奇数个数和后n个数的偶数个数
  • l2为前n个数的偶数个数和后n个数的奇数个数
  • 知道这个后方案数自然很容易求
  • ans1=(ans1+f[l1][i]%mo*f[l1][i])%mo; ans2=(ans2+f[l2][i]%mo*f[l2][i])%mo;
  • 答案为ans-ans1*ans2
  • 注意要取膜

代码

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 using namespace std;
 5 const int mo=999983;
 6 int n,len,a[15];
 7 long long ans1,ans2,sum,f[1005][1005*9];
 8 char s[15];
 9 int main()
10 {
11     scanf("%d",&n);
12     scanf("%s",s+1);
13     len=strlen(s+1);
14     for (int i=1;i<=len;i++) a[i]=s[i]-'0';
15     f[0][0]=1;
16     for (int i=1;i<=n;i++)
17         for (int j=0;j<=i*9;j++)
18             for (int k=1;k<=len;k++)
19                 if (j-a[k]>=0)
20                     f[i][j]=(f[i][j]+f[i-1][j-a[k]])%mo;
21     for (int i=0;i<=n*9;i++) sum=(sum+2*f[n][i]%mo*f[n][i])%mo;
22     int l1=(n+1)/2,l2=n/2;
23     for (int i=0;i<=l1*9;i++) ans1=(ans1+f[l1][i]%mo*f[l1][i])%mo;
24     for (int i=0;i<=l2*9;i++) ans2=(ans2+f[l2][i]%mo*f[l2][i])%mo;
25     printf("%lld",(sum-ans1*ans2%mo+mo)%mo);
26 }

 

转载于:https://www.cnblogs.com/Comfortable/p/8562344.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值