1. 程式人生 > >[容斥原理][dp]JZOJ P3056 數字

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

clas ++ 一個 set AR iostream 註意 ace 相等

【問題描述】


一個數字被稱為好數字當他滿足下列條件:


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 }

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