【BZOJ1072】【SCOI2007】排列 [状压DP]

排列

Time Limit: 10 Sec  Memory Limit: 128 MB
[Submit][Status][Discuss]

Description

  给一个数字串s和正整数d, 统计s有多少种不同的排列能被d整除(可以有前导0)。

  例如123434有90种排列能被2整除,其中末位为2的有30种,末位为4的有60种。

Input

  输入第一行是一个整数T,表示测试数据的个数,以下每行一组s和d,中间用空格隔开。

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

  s的长度不超过10, 1<=d<=1000, 1<=T<=15

Solution

  我们运用状压DP令 f[j][opt] 表示当前余数为 j,状态为opt的方案

  状态记录的是:各个数字被用了几次。

  那么我们就可以状压了。先DFS出每个状态,记sum[k]表示后缀积,那么显然 从 opt 转移到 第k个数字多用一次的状态 就是 opt + sum[k + 1]

Code

#include<iostream>    
#include<string>    
#include<algorithm>    
#include<cstdio>    
#include<cstring>    
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;  
typedef long long s64;

const int ONE = 20005;

int T, n, m;
int num;
int vis[ONE], Num[20], sum[20];
int f[1005][20005];
int Sta[ONE][10];
char ch[ONE];

int get()
{
        int res=1,Q=1;char c;
        while( (c=getchar())<48 || c>57 ) 
        if(c=='-')Q=-1; 
        res=c-48;     
        while( (c=getchar())>=48 && c<=57 )
        res=res*10+c-48;    
        return res*Q;
}

void Dfs(int T)
{
        if(T > 10)
        {
            num++;
            for(int i = 0; i <= 9; i++)
                Sta[num][i] = vis[i];
            return;
        }
        
        for(int i = 0; i <= Num[T]; i++)
            vis[T] = i, Dfs(T + 1);
}

void Deal()
{
        memset(f, 0, sizeof(f));
        memset(Num, 0, sizeof(Num));
        memset(sum, 0, sizeof(sum));
        num = 0;
        scanf("%s", ch + 1);    m = get();
        n = strlen(ch + 1);
        
        for(int i = 1; i <= n; i++) Num[ch[i] - '0']++;
        sum[10] = 1; for(int i = 9; i >= 0; i--) sum[i] = sum[i + 1] * (Num[i] + 1);
        
        Dfs(0);
        
        f[0][1] = 1;
        for(int opt = 1; opt <= num; opt++)
            for(int j = 0; j < m; j++)
                if(f[j][opt])
                    for(int k = 0; k <= 9; k++)
                    {
                        if(Sta[opt][k] >= Num[k]) continue; 
                        int to = opt + sum[k + 1];
                        f[(j * 10 + k) % m][to] += f[j][opt];
                    }
        
        printf("%d\n", f[0][num]);
}

int main()
{
        T = get();
        while(T--)
            Deal();
}
View Code

 

posted @ 2017-09-25 18:30  BearChild  阅读(225)  评论(0编辑  收藏  举报