bzoj1072 [SCOI2007]排列perm
给一个数字串s和正整数d, 统计s有多少种不同的排列能被d整除(可以有前导0)。例如123434有90种排列能
被2整除,其中末位为2的有30种,末位为4的有60种。
Input
输入第一行是一个整数T,表示测试数据的个数,以下每行一组s和d,中间用空格隔开。s保证只包含数字0, 1
, 2, 3, 4, 5, 6, 7, 8, 9.
Output
每个数据仅一行,表示能被d整除的排列的个数。
Sample Input
7 000 1 001 1 1234567890 1 123434 2 1234 7 12345 17 12345678 29
Sample Output
1 3 3628800 90 3 6 1398
Hint
在前三个例子中,排列分别有1, 3, 3628800种,它们都是1的倍数。
【限制】
100%的数据满足:s的长度不超过10, 1<=d<=1000, 1<=T<=15
题解:这道题,还是状压的题目,可以状压,当前选了哪几个数,这样节省了不少空间,时间,貌似全排列可以过,
然后状压好打,最后需要处理,有重复的问题。
然后就转化为了有重复元素的全排列,状态记录为,选了i状态,模数为j的方案数。
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 const int N=1e6+10,M=1e3+11,inf=1e9+7,mod=1e9+7; 8 9 int n; 10 int dp[1<<11][M],d,T,p[N],c[N]; 11 char a[N]; 12 13 int main() 14 { 15 c[0]=c[1]=1; 16 for(int i=2;i<=11;i++) c[i]=c[i-1]*i; 17 scanf("%d",&T); 18 while(T--) 19 { 20 scanf("%s%d",a,&d); 21 n=strlen(a); 22 memset(p,0,sizeof(p)); 23 for(int i=0;i<n;i++) p[a[i]-'0']++; 24 memset(dp,0,sizeof(dp)); 25 int U=(1<<n)-1; 26 dp[0][0]=1; 27 for(int i=0;i<=U;i++) 28 for(int j=0;j<n;j++) 29 if(!(i&(1<<j))) 30 { 31 for(int k=0;k<d;k++) 32 dp[i|(1<<j)][(k*10+(a[j]-'0'))%d]+=dp[i][k]; 33 } 34 int ans=dp[U][0]; 35 for(int i=0;i<10;i++) 36 ans/=c[p[i]]; 37 printf("%d\n",ans); 38 } 39 }