[SCOI2007] 排列
阿新 • • 發佈:2018-07-17
輸入輸出 一行 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] 排列