题目
首先定义什么是迷惑数字。
对于一个 2 n 2n 2n 位的数字 X X X,将其 随机排列 后划分成两个数字,它的前 n n n 位构成数字 A A A,后 n n n 位构成数字 B B B. 如果 A + B A+B A+B 是 10 10 10 的幂,则数字 X X X 是迷惑数字。注意 A A A 和 B B B 可能有前导 0 0 0.
比如 46 46 46 是一个迷惑数字 ( 4 + 6 = 10 ) (4+6=10) (4+6=10), 9820 9820 9820 是一个迷惑数字 ( 98 + 02 = 100 ) (98+02=100) (98+02=100), 08362090 08362090 08362090 也是一个迷惑数字 ( 6020 + 3980 = 10000 ) (6020+3980=10000) (6020+3980=10000).
现在给你一个 2 n 2n 2n 位的数字,其中有些数位丢失了,丢失的数位用 ‘?’ 表示。你需要统计将这些问号替换成数字以获得迷惑数字的方案数。
数据范围
对于 10 % 10\% 10% 的数据,数字串长度 ≤ 20 \le 20 ≤20. 问号最多 8 8 8 个。
对于 30 % 30\% 30% 的数据,数字串长度 ≤ 1 0 5 \le 10^5 ≤105. 问号最多 8 8 8 个。
对于 100 % 100\% 100% 的数据,数字串长度 ≤ 1 0 5 \le 10^5 ≤105. 问号最多 1000 1000 1000 个。
题解
考虑一下
a
+
b
=
1
0
n
a+b=10^n
a+b=10n,
a
,
b
a,b
a,b 需要满足什么条件。
考虑加法的实质。我们将a,b逐位相加时,受到后面进位的影响最多只有
1
1
1,而当前位置相加最大值为
(
9
+
9
+
1
(
进
位
)
)
%
10
=
9
(9+9+1(进位))\%10=9
(9+9+1(进位))%10=9,不可能进位2次,所以要满足除了第一位是1,其他位都是0,就必须满足前面若干位加起来为
9
9
9,中间一位加起来为
10
10
10,后面若干位加起来位
0
0
0(即
0
+
0
0+0
0+0)。
所以
1
1
1和
8
8
8 必须数量相等(匹配),
2
2
2 和
7
7
7 必须数量相等
…
\dots
… 综上我们可以用dp计数来解决这道题:
f
[
i
]
[
j
]
f[i][j]
f[i][j] 表示前i个匹配(即
(
1
,
8
)
,
(
2
,
7
)
,
(
3
,
6
)
,
(
4
,
5
)
(1,8),(2,7),(3,6),(4,5)
(1,8),(2,7),(3,6),(4,5),最多4个(
i
≤
4
i \leq 4
i≤4)),用
j
j
j 个问号,的合法匹配方案数,然后类似背包转移即可。
注意事项
由于 0 0 0 和 9 9 9处理起来比较麻烦,我们可以先枚举 0 0 0 和 9 9 9,再进行上述操作。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=5e5+10;
const int M=1005;
const int P=1e9+7;
char ch[N];
ll m,f[7][M],c[M][M],cnt[15],ans;
int fu[N],tot[N];
int main(){
scanf("%s",ch+1);
int len=strlen(ch+1);
for(int i=1;i<=len;++i){
if(ch[i]=='?')m++;
else tot[ch[i]-'0']++;
}
for(int i=0;i<=m;++i){
c[i][0]=1;
for(int j=1;j<=m;++j)c[i][j]=(c[i-1][j-1]+c[i-1][j])%P;
}
//cout<<"FAQ "<<m<<endl;
for(int x=1,y=9;x<=5;++x,--y){
for (int i=1;i<=5;i++)for( int j=0;j<=m;j++)f[i][j]=0;
for(int i=0;i<=9;++i){
fu[i]=0;
cnt[i]=tot[i]-(i==x)-(i==y);
if(cnt[i]<0)fu[i]-=cnt[i],cnt[i]=0;
}
for(int x1=fu[0];x1<=m;++x1){
for(int x2=fu[9];x2+x1<=m;++x2){
int X1=x1+cnt[0],X2=x2+cnt[9]-fu[9];
if(X1>=X2&& !((X1-X2)&1))f[1][x1+x2]=(f[1][x1+x2]+c[m][x1]*c[m-x1][x2]%P)%P;
}
}
// for(int i=1;i<=m;++i)cout<<i<<" "<<f[1][i]<<endl;
for(int i=1,j=8;i<5;++i,--j){
int f1=(fu[i]+max(cnt[j]-cnt[i],1ll*0)),f2=(fu[j]+max(cnt[i]-cnt[j],1ll*0));
for(;f1+f2<=m;f1++,f2++){
for(int k=0;k+f1+f2<=m;++k)f[i+1][k+f1+f2]=(f[i+1][k+f1+f2]+f[i][k]*c[m-k][f1]%P*c[m-k-f1][f2]%P)%P;
}
}
ans=(ans+f[5][m])%P;
}
printf("%lld\n",ans);
return 0;
}