[SCOI2007] 排列

Posted cute-hzy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[SCOI2007] 排列相关的知识,希望对你有一定的参考价值。

题目描述

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

被2整除,其中末位为2的有30种,末位为4的有60种。

输入输出格式

输入格式:

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

, 2, 3, 4, 5, 6, 7, 8, 9.

输出格式:

每个数据仅一行,表示能被d整除的排列的个数。

输入输出样例

输入样例#1:

7
000 1
001 1
1234567890 1
123434 2
1234 7
12345 17
12345678 29

输出样例#1:

1
3
3628800
90
3
6
1398

100%的数据满足:s的长度不超过10, 1<=d<=1000, 1<=T<=15.

题解

状压dp经典题,用(f(S, j))表示用了集合S中的点,现在余数是j的方案数.

转移比较容易:(f(S cup i, (k * 10 + s_i)mod d) += f(S, k))

最后解决重复,除以(0)~(9)每个数出现次数的阶乘就行啦.

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

namespace INPUT {
    const int L = 1 << 15;
    char _buf[L], *S, *T, c;
    char _gc() {
        if(S == T) {
            T = (S=_buf) + fread(_buf, 1, L, stdin);
            if(S == T) return EOF;
        }
        return *S ++;
    }

    template<typename T> void write(T x) {
        if(x < 0) x = -x, putchar('-');
        static char buf[31];
        int p(0);
        do buf[p ++] = x % 10 + '0', x /= 10;
        while(x);
        for(int i=p-1; ~i; i--) putchar(buf[i]);
    }

    template<typename T> T Read() {
        T ans(0);
        bool Sign = false;
        while(!isdigit(c = _gc()) && c != '-');
        if(c == '-') Sign = true, c = _gc();
        do ans = ans * 10 + c - 48;
        while(isdigit(c=_gc()));
        return Sign ? -ans : ans;
    }
};
using namespace INPUT;

#define MAXN 10
#define MAXD 1000

int f[1 << MAXN][MAXD], s[MAXN], d, n, ans;
int cl[MAXN + 1], fc[MAXN + 1];

void Init_fc() {
    fc[0] = 1;
    for(int i=1; i<=MAXN; i++) 
        fc[i] = fc[i-1] * i;
}

int main() {
    Init_fc();
    int T = Read<int>(), ans;
    while(T --) {
        while(!isdigit(c = _gc())) ;
        for(n = 0; isdigit(c); c = _gc()) s[n ++] = c - '0';
        d = Read<int>();
        memset(f, 0, sizeof f);
        f[0][0] = 1;
        for(int S=0; S<1<<n; S ++) {
            for(int k=0; k<d; k++) if(f[S][k])
                for(int i=0; i<n; i++) if(!(S & 1 << i))
                    f[S | 1 << i][(k * 10 + s[i]) % d] += f[S][k];
        }
        ans = f[(1<<n)-1][0];
        memset(cl, 0, sizeof cl);
        for(int i=0; i<n; i++) cl[s[i]] ++;
        for(int i=0; i<10; i++) ans /= fc[cl[i]];
        write<int>(ans);
        putchar('
');
    }
    return 0;
}

以上是关于[SCOI2007] 排列的主要内容,如果未能解决你的问题,请参考以下文章

P4163 [SCOI2007]排列(状压dp)

[BZOJ1072][SCOI2007]排列perm

bzoj1072SCOI2007排列perm

BZOJ1072: [SCOI2007]排列perm 状压DP

[BZOJ1072][SCOI2007]排列perm 状压dp

[BZOJ1072][SCOI2007]排列perm