1. 程式人生 > >[SCOI2007] 排列

[SCOI2007] 排列

輸入輸出 一行 stdin lse for pen sign lin +=

題目描述

給一個數字串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('\n');
    }
    return 0;
}

[SCOI2007] 排列