D. Roman and Numbers(状压dp)

n n n的每一位状压,问题等价于选择一个顺序走完这 c n t cnt cnt个位使得 ( m o d m ) = 0 \pmod{m}=0 (modm)=0答案。

d p [ i ] [ j ] dp[i][j] dp[i][j]表示状态 i i i m m m j j j的答案。

转移时需要注意最高位对应的数不能为 0 0 0

初始化: d p [ 0 ] [ 0 ] = 1 dp[0][0]=1 dp[0][0]=1​。

而且同一个数字不能在一个状态转移多次,所以可以用一个标记数组,标记哪些数字已经转移过了。

时间复杂度: O ( 10 m × 2 18 ) O(10m\times 2^{18}) O(10m×218)

// Problem: D. Roman and Numbers
// Contest: Codeforces - Codeforces Round #235 (Div. 2)
// URL: https://codeforces.ml/problemset/problem/401/D
// Memory Limit: 512 MB
// Time Limit: 4000 ms
// Date: 2021-08-11 19:32:34
// --------by Herio--------

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull; 
const int N=1e3+5,M=(1<<18)+1,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define IOS ios::sync_with_stdio(false),cin.tie(0) 
void Print(int *a,int n){
	for(int i=1;i<n;i++)
		printf("%d ",a[i]);
	printf("%d\n",a[n]); 
}
ll dp[M][101];
int a[20],cnt,vis[20];
int main(){
	ll n;int m;scanf("%lld%d",&n,&m);
	while(n) a[++cnt]=n%10,n/=10;
	int st=1<<cnt;
	dp[0][0]=1;
	for(int i=0;i<st;i++){
		mst(vis,0);
		for(int j=0;j<cnt;j++){
			if((1<<j)==i&&!a[j+1]) continue;
			if(vis[a[j+1]]||!(i>>j&1)) continue;
			vis[a[j+1]]=1;
			for(int k=0;k<m;k++)
				dp[i][(k*10+a[j+1])%m]+=dp[i^(1<<j)][k];
		}
	}
	printf("%lld\n",dp[st-1][0]);
	return 0;
}