• 【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
    ", f[0][num]);
    }
    
    int main()
    {
            T = get();
            while(T--)
                Deal();
    }
    View Code

     

  • 相关阅读:
    Pandas学习整理与实践
    数据描述性统计整理
    关于购置硬盘的相关注意点
    福大软工 · 最终作业
    福大软工 · 第十二次作业
    Beta冲刺 (7/7)
    Beta冲刺 (6/7)
    深度剖析Vue中父给子、子给父、兄弟之间传值!
    mysql 增删改插
    前端必学TypeScript之第一弹,st基础类型!
  • 原文地址:https://www.cnblogs.com/BearChild/p/7593272.html
Copyright © 2020-2023  润新知