[SCOI2007]排列(状压DP)

题目

题目描述
给一个数字串s和正整数d, 统计s有多少种不同的排列能被d整除(可以有前导0)。例如123434有90种排列能

被2整除,其中末位为2的有30种,末位为4的有60种。

题解

  • f [ S ] [ i ] f[S][i] f[S][i]表示当前选的数的集合为 S S S,对 d d d取余的余数为 i i i
  • 考虑转移设新加的数的位置为 x x x那么 f [ S   o r   x ] [ ( i ∗ 10 + a [ x ] )   m o d   d ] + = f [ S ] [ i ] f[S \ or \ x][(i*10+a[x])\bmod d] +=f[S][i] f[S or x][(i10+a[x])modd]+=f[S][i]
  • 算答案时除去重复即可

code

#include <bits/stdc++.h> 

using namespace std; 

const int N = (1 << 10) + 100; 
const int M = 1e3 + 10; 
const int Z = 10; 

int T, n, d; 
char ch[N]; 
int cnt[Z], fac[Z]; 
int f[N][M]; 

int main() {
	int T; scanf("%d", &T);
	while (T--) {
		scanf("%s %d", ch + 1, &d); 
		n = strlen(ch + 1); 
		for (int i = 0; i <= 9; ++i) cnt[i] = 0, fac[i] = 1; 
		for (int i = 1; i <= n; ++i) cnt[(int)(ch[i]-'0')]++; 
		for (int i = 0; i <= 9; ++i) 
			for (int j = 1; j <= cnt[i]; ++j) 
				fac[i] *= j; 

		memset(f, 0, sizeof(f)); 
		f[0][0] = 1; 

		for (int s = 0; s < (1 << n); ++s) {
			for (int j = 0; j < d; ++j) {
				for (int i = 1; i <= n; ++i) {
					if (s & (1<<(i-1))) continue; 
					f[s|(1<<(i-1))][(10*j+(int)(ch[i]-'0'))%d] += f[s][j]; 
				}
			}
		}

		int ans = f[(1<<n)-1][0]; 
		for (int i = 0; i <= 9; ++i) ans /= fac[i]; 

		printf("%d\n", ans); 
	}
	return 0; 
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值