[ SCOI 2007 ] Perm

\(\\\)

\(Description\)


给出只包括多个\(0\text~ 9\)的数字集,求有多少个本质不同的全排列,使得组成的数字能够整除\(M\)

  • \(|S|\in [1,10]\)\(M\in [1,10^3]\)

\(\\\)

\(Solution\)


  • 一眼状压,先将所有数字看作互不相同,\(f[S][k]\)表示集合内数字选取情况为\(S\),当前组成的数对\(M\)取模的结果为\(k\)的方案数,显然边界\(f[0][0]=1\)
  • 枚举补集里的元素扩展,每次注意余数改为\((k*10+a[j])\%M\)
  • 注意到每一个答案最后都用到了所有的数,所以所有相同的数最后在每一个答案里都会出现全排列,也就是说答案为\(f[S_{max}][0]\)除掉每一个数的全排列后的答案。

\(\\\)

\(Code\)


#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 1050
#define R register
#define gc getchar
using namespace std;
 
int n,m,t,tot,a[10],p[10],num[10],f[N][N];
 
int main(){
  scanf("%d",&t);
  while(t--){
    char c=gc(); m=0;
    for(R int i=0;i<=9;++i) p[i]=1,num[i]=0;
    while(!isdigit(c)) c=gc();
    a[tot=1]=c^48; ++num[a[1]];
    while(isdigit(c=gc())) ++num[a[++tot]=c^48];
    for(R int i=0;i<=9;++i)
      for(R int j=1;j<=num[i];++j) p[i]*=j;
    while(!isdigit(c)) c=gc();
    while(isdigit(c)){
      m=(m<<1)+(m<<3)+(c^48); c=gc();
    }
    for(R int i=0;i<(1<<tot);++i)
      for(R int j=0;j<m;++j) f[i][j]=0;
    f[0][0]=1;
    for(R int i=0;i<(1<<tot);++i)
      for(R int j=0;j<m;++j) if(f[i][j]){
        for(R int k=0;k<tot;++k)
          if(!(i&(1<<k))) f[i|(1<<k)][(j*10+a[k+1])%m]+=f[i][j];
      }
    for(R int i=0;i<=9;++i) f[(1<<tot)-1][0]/=p[i];
    printf("%d\n",f[(1<<tot)-1][0]);
  }
  return 0;
}
posted @ 2018-09-05 09:20  SGCollin  阅读(133)  评论(0编辑  收藏  举报