卧薪尝胆,厚积薄发。
提高A组模拟 number
Date: Sat Aug 18 18:24:29 CST 2018
In Category:
NoCategory
Description:
给定正整数
$ n,m$
,问有多少个正整数满足:
$(1)$
不含前导
$ 0$
;
$ (2)$
是
$ m $
的倍数;
$ (3)$
可以通过重排列各个数位得到
$ n$
。
$1\le n \le10^{20}$
$1\le m \le 100$
Solution:
数位
$DP$
,第三个条件表示
$k$
中各个数字个数与
$n$
相同,
$f[i][j]$
表示剩余
$n$
中的数字个数状压起来为
$i$
,对
$m$
取模结果是
$j$
的方案数,最大的问题是怎么状压
$i$
,因为数字最大的出现个数可能是
$20$
,如果用
$map$
维护
$vector$
最慢的点跑到
$12s$
,于是可以以
$n$
中每一位的个数
$+1$
作为每一位的进制把状态压成一个数,就可以开数组了。
Code:
#pragma GCC optimize(3)
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<map>
#include<vector>
#include<cctype>
#include<cstring>
using namespace std;
int m;
int tot;
#define MOD 998244353
int cntbit[10];
inline int encode(int kcnt[])
{
register int res = 0;
for(register int i = 0;i <= 9;++i)
res = res * (cntbit[i] + 1) + kcnt[i];
return res;
}
int f[60000][101];
bool v[60000][101];
int dfs(int pos,bool zero,int k[],int mod)
{
if(pos == 0)
{
if(mod == 0)return 1;
else return 0;
}
register int code = encode(k);
if(v[code][mod])return f[code][mod];
register long long res = 0;
for(register int i = 0;i <= 9;++i)
{
if(!zero && i == 0)continue;
if(k[i] == 0)continue;
--k[i];
res += dfs(pos - 1,true,k,(mod * 10 + i) % m);
++k[i];
}
res %= MOD;
f[code][mod] = res;v[code][mod] = true;
return res;
}
int stcnt[10];
int main()
{
freopen("number.in","r",stdin);
freopen("number.out","w",stdout);
register char c = getchar();
while(c < '0' || c > '9')c = getchar();
register int k;
while('0' <= c && c <= '9')
{
++tot;
k = (c ^ 48);
stcnt[k] += 1;
cntbit[k] += 1;
c = getchar();
}
scanf("%d",&m);
printf("%d\n",dfs(tot,false,stcnt,0));
fclose(stdin);
fclose(stdout);
return 0;
}
In tag:
DP-数位DP
Copyright © 2020
wjh15101051
ღゝ◡╹)ノ♡