状压DP之排列perm

时间:2020-06-26 19:43:52   收藏:0   阅读:75

题目

[SCOI2007]排列
给一个数字串s和正整数d, 统计s有多少种不同的排列能被d整除(可以有前导0)。例如123434有90种排列能被2整除,其中末位为2的有30种,末位为4的有60种。

输入格式

输入第一行是一个整数T,表示测试数据的个数,以下每行一组s和d,中间用空格隔开。s保证只包含数字0, 1, 2, 3, 4, 5, 6, 7, 8, 9.

输出格式

每个数据仅一行,表示能被d整除的排列的个数。

样例

样例输入

7
000 1
001 1
1234567890 1
123434 2
1234 7
12345 17
12345678 29

样例输出

1
3
3628800
90
3
6
1398

数据范围与提示

在前三个例子中,排列分别有1, 3, 3628800种,它们都是1的倍数。
100%的数据满足:s的长度不超过10, 1<=d<=1000, 1<=T<=15。

思路

上代码



#include<bits/stdc++.h>
using namespace std;
const int maxn=1<<11,maxm=1000+10;
int f[maxn][maxm];//f[i][j]表示状态i余数为j的排序个数
int T,mod;
int a[maxn],cnt[maxn];//cnt记录某个数i出现的次数
char s[15];
int J[]={0,1,2,6,24,120,720,5040,40320,362880,3628800};
int main(){
	scanf("%d",&T);
	while(T--){
		memset(cnt, 0, sizeof(cnt));//一定记得初始化
        memset(f, 0, sizeof(f));
		scanf("%s%d",s,&mod);
		int len=strlen(s);
		for(int i=0;i<len;i++){
			a[i+1]=s[i]-‘0‘;
			cnt[a[i+1]]++;//记录a[i+1]出现次数
		}
		int lim=1<<len;//记录界限
		f[0][0]=1;//初始化临界状态,0状态下余数为0的有1种情况
		for(int i=0;i<lim;i++){//枚举状态
			for(int k=0;k<mod;k++){//枚举余数
				if(f[i][k]){//假如没有跳过
					 for(int j=0; j<len; j++)//枚举插入的数字
                   		               if( (i & (1<<j)) == 0 )//无交集
                                                      f[(i|(1<<j))][(k*10+a[j+1])%mod] += f[i][k];//叠加
				}
			}
		}
		int ans=f[lim-1][0];
		for(int i=0; i<=9; i++) if( cnt[i] ) ans /= J[cnt[i]];//去重
		cout<<ans<<endl;		
	}
}

原文:https://www.cnblogs.com/soda-ma/p/13196026.html

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!