bzoj 1072: [SCOI2007]排列perm

Description

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

solutio

这题复杂负很诡,爆搜可以过去
我写的状压DP,复杂度和搜索一样,\(O(10!*10*d* T)\)
设状态为 \(f[i][j]\) 表示已经选了序列中的位置状态为 \(i\) ,mod d等于 \(j\) 的方案数
转移运用读入优化的方法,直接 \(*10+a[i]\) 取模就行

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
const int N=15;
int f[1<<10][1005],t[15],mul[N];char s[N];
void work()
{
   scanf("%s",s);
   int n=strlen(s),d;
   int lim=(1<<n)-1;
   RG int i,j,k;
   scanf("%d",&d);
   f[0][0]=1;
   for(i=0;i<lim;i++){
      for(j=0;j<d;j++){
         if(!f[i][j])continue;
         for(k=0;k<n;k++){
            if((1<<k)&i)continue;
            f[i|(1<<k)][(j*10+s[k]-48)%d]+=f[i][j];
         }
      }
   }
   for(i=0;i<n;i++)t[s[i]-48]++;
   for(i=0;i<=9;i++)if(t[i])f[lim][0]/=mul[t[i]],t[i]=0;
   printf("%d\n",f[lim][0]);
   memset(f,0,sizeof(f));
}
 
int main()
{
    int T;cin>>T;
   mul[0]=1;for(int i=1;i<=10;i++)mul[i]=mul[i-1]*i;
   while(T--)work();
    return 0;
}

posted @ 2017-10-25 12:23  Hxymmm  阅读(105)  评论(0编辑  收藏  举报