1. 程式人生 > >【BZOJ1072/SCOI2007】排列perm

【BZOJ1072/SCOI2007】排列perm

                                 1072: [SCOI2007]排列perm

                                                  Time Limit: 10 Sec  Memory Limit: 128 MB                                                             Submit: 3111  Solved: 1959

Description

  給一個數字串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

解析:

       狀壓DP。

       直接列舉的話根據題意是求得組合而不是排列。。。

       所以處以每個數出現次數得階乘才是最終答案。

程式碼:

#include <bits/stdc++.h>
using namespace std;

int t,n,m,f[1<<10][1005],bin[10],num[10],sum[10],a[1<<10][10];
char ch[11];

inline void solve()
{
	for(int i=0;i<=9;i++) bin[i]=1,sum[i]=0;
	for(int i=0;i<n;i++) sum[num[i]]++,bin[num[i]]*=sum[num[i]];
	memset(f,0,sizeof(f));f[0][0]=1;
	for(int i=0;i<(1<<n);i++)
	  for(int j=0;j<m;j++)
	    for(int k=0;k<n;k++)
	      if(!((1<<k)&i))
	        f[i|(1<<k)][(j*10+num[k])%m]+=f[i][j];
	for(int i=0;i<10;i++) f[(1<<n)-1][0]/=bin[i];
}

int main()
{
	scanf("%d",&t);
	while(t--)
	{
	  scanf("%s%d",ch,&m);
	  n=strlen(ch);
	  for(int i=0;i<n;i++) num[i]=ch[i]-'0';
	  solve();
	  printf("%d\n",f[(1<<n)-1][0]);
	}
	return 0;
}