題目描述
偶然在CSDN看到有人寫了CCSP2019T2_紙牌計數的題解,突然想起來是一個不錯的計數、dp題,
以前的U盤找不到了,記得當時存了一步步偏分到AC代碼,可惜,又想起來18年打鐵了,,,
此人的題解的鏈接 CCSP201902紙牌計數——解題報告
當年一共有5題,取自:https://www.sohu.com/a/347851686_610300
T1: 摘水果 fruit
T2:紙牌計數 card
T3:系統實作題 SQL 查詢
T4:系統策略題 調度器 scheduler
T5:系統結構體 評測魚 risc-v
T2的題面:
Description
我們有一副紙牌,它由 n 張牌組成,其中每張牌上標有一個數字(0 到 9)和一個大寫字母(A 到 Z),例如 2C、1F,
我們如下定義一個字串與一張牌之間的匹配關系:
字串 ?? 與任何一張牌都匹配,
第一位為 ? 而第二位為字母的字串,與任何一張標有該字母的牌匹配,例如,字串 ?C 與任何標有 C 的牌匹配,
第一位為數字而第二位為字母的字串,僅與內容完全一致的牌匹配,例如,字串 0C 與內容為 0C 的牌匹配,
不會出現第一位為數字而第二位為 ? 的字串,
我們稱 m 個字串 S1 ... Sm 與 m 張牌 C1 ... Cm 是匹配的,當且僅當:存在集合 { 1 , … , m } 上的一一映射 σ,使得對于任意 1 ≤ i ≤ m ,Si 與 C_σ(i) 匹配,
例如,對于 ?? 和 ?C 這兩個字串,可以匹配內容為 1C 和 2C 的牌,因為字串 ?? 與內容為 2C 的牌匹配且字串 ?C 與內容為 1C 的牌匹配,而 ?? 和 ?C 這兩個字串不能匹配內容為 1S 和 1P 的牌,
現在,給定 m 個字串,你需要求解:如果在 n 張牌中(無放回地)隨機選取 m 張,有多大概率與這些字串匹配?
Input
每個輸入檔案包含多個輸入資料,
第一行輸入該檔案的資料個數 T,
接下來依次輸入每個資料,每個資料包含三行,其中:
第一行輸入兩個正整數 n 和 m;
第二行輸入以空格分隔的 n 個字串,表示每張紙牌的內容(每個字串第一位為數字,第二位為大寫字母);
第三行輸入以空格分隔的 m 個字串,表示每個需要匹配的字串(每個字串第一位為數字,第二位為大寫字母;或第一位為 ?,第二位為大寫字母;或為 ??),
Output
對于每個輸入資料,輸出一行,表示所求的概率,概率需要以最簡分數 u/v 的形式輸出,其中 0 ≤ u ≤ v 且 u, v 為互質的整數,
特殊地,對于 0 請輸出 0/1,對于 1 請輸出 1/1,
Subtasks
對于所有的資料,1 <= m <= n <= 60, T <= 1000, $1 \leq m \leq n \leq 60; T \leq 1000$,
(11分)m=1;
(23分)m=n;
(27分)n=30 且所有的牌為 0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0S 1S 2S 3S 4S 5S 6S 7S 8S 9S 0P 1P 2P 3P 4P 5P 6P 7P 8P 9P;
(22分)n=40 且所有的牌為 0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0S 1S 2S 3S 4S 5S 6S 7S 8S 9S 0P 1P 2P 3P 4P 5P 6P 7P 8P 9P;
(17分)沒有特殊的限制,
題解
一時間不記得咋寫的了,按記憶口胡了一個做法,感覺復雜度有點假,但好像跑不滿,以后再看看吧:
- 求概率/期望的時候,兩個獨立事件(即正交的情況)可以分開考慮,答案為每個事件概率的乘積,類似于積性函式中的一些理念,
- n張牌的字母都是固定的,一般m個字串的字母也是固定的(除非是兩個問號??),
- 考慮是否可以先分26個字母考慮,再考慮??,
- rdp[j][k] 表示考慮到數字j用了k個問號的(不)合法情況數(合不合法,即是否存在重復計數的),把單個?的情況用組合數計數分配一下
- 現在 ?? 還沒處理吧,所以??可以在這個層次上再套一層dp
- val[i][w] 表示考慮到字母i已經用了w個??的合法方案數,最后背包dp一遍求答案
- (不確定有沒有爆 long long,感覺復雜度可以減掉一個60?
#include <bits/stdc++.h>
#define fi first
#define se second
#define o2(x) (x) * (x)
#define mk make_pair
#define eb emplace_back
#define SZ(x) ((int)(x).size())
#define all(x) (x).begin(), (x).end()
#define clr(a, b) memset((a), (b), sizeof((a)))
#define rep(i, s, t) for(register int i = (s), LIM=(t); i < LIM; ++i)
#define per(i, s, t) for(register int i = (s), LIM=(t); i > LIM; --i)
#define GKD std::ios::sync_with_stdio(false);cin.tie(0)
#define my_unique(x) sort(all(x)), x.erase(unique(all(x)), x.end())
using namespace std;
typedef long long LL;
typedef long long int64;
typedef unsigned long long uint64;
typedef pair<int, int> pii;
// mt19937 rng(time(NULL));//std::clock()
// mt19937_64 rng64(chrono::steady_clock::now().time_since_epoch().count());
// shuffle(arr, arr + n, rng64);
inline int64 read() {
int64 x = 0;int las = 0;char ch = getchar();
while (ch < '0' || ch > '9') las |= (ch == '-'), ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch =
getchar(); return x = las ? -x : x;
}
inline void write(int64 x, bool las = true) {
if (x == 0) {putchar('0'); if(las)putchar('\n');else putchar(' ');return;}
if (x < 0) {putchar('-');x = -x;}
static char s[23];
int l = 0;
while (x != 0)s[l++] = x % 10 + 48, x /= 10;
while (l)putchar(s[--l]);
if(las)putchar('\n');else putchar(' ');
}
int lowbit(int x) { return x & (-x); }
template <class T>
T big(const T &a1, const T &a2) {return a1 > a2 ? a1 : a2;}
template <class T>
T sml(const T &a1, const T &a2) {return a1 < a2 ? a1 : a2;}
template <typename T, typename... R>
T big(const T &las, const R &... r) {return big(las, big(r...));}
template <typename T, typename... R>
T sml(const T &las, const R &... r) {return sml(las, sml(r...));}
void debug_out() { cout << '\n'; }
template <typename T, typename... R>
void debug_out(const T &las, const R &... r) {
cout << las << " ";
debug_out(r...);
}
#ifdef LH_LOCAL
#define debug(...) cout << "[" << #__VA_ARGS__ << "]: ", debug_out(__VA_ARGS__);
#else
#define debug(...) ;
#endif
#define debug(...) ;
/*================Header Template==============*/
const int mod = 998244353;// 998244353
int ksm(int a, int64 b, int kmod = mod) {int res = 1;for(;b > 0;b >>= 1, a = (int64)a * a % kmod) if(b &1) res = (int64)res * a % kmod;return res;}
const int INF = 0x3f3f3f3f;
const int MXN = 2e5 + 5;
const int MXM = 5e6 + 7;
int n, m;
char card[65][3], str[65][3];
// 0 <= . <= 9, A <= . <= Z -> 1 <= . <= 10
int cnt[30][15], need[30][15];
int sum[2][30];
LL fdp[30], rdp[15][65], val[30][65], dp[30][65];
LL COMB(int n, int m) {
if(n == m) return 1;
if(n < m) return 0;
LL res = 1;
for(int i = 0; i < m; ++i) {
res *= (n - i);
res /= i + 1;
}
return res;
}
void work() {
n = read(), m = read();
for(int i = 1; i <= n; ++i) scanf("%s", card[i]);
for(int i = 1; i <= m; ++i) scanf("%s", str[i]);
int flag = 1;
for(int i = 1; i <= n; ++i) {
cnt[card[i][1] - 'A'][card[i][0] - '0' + 1] ++;
sum[0][card[i][1] - 'A'] ++;
}
LL ww = m;
for(int i = 1; i <= m; ++i) {
if(str[i][1] != '?') {
if(str[i][0] != '?') need[str[i][1] - 'A'][str[i][0] - '0' + 1] ++;
else need[str[i][1] - 'A'][0] ++;
sum[1][str[i][1] - 'A'] ++;
ww --;
}
}
LL res = 1;
for(int i = 0; i < 26; ++i) {
if(sum[0][i] < sum[1][i]) {
flag = 0;
break;
}
for(int w = 0; w <= ww; ++w) {
need[i][0] += w;
fdp[i] = 1;
memset(rdp, 0, sizeof(rdp));
rdp[0][0] = 1;
// rdp[j][k] 表示考慮到數字j用了k個問號的(不)合法情況數(合不合法,即是否存在重復計數的)
// 把單個?的情況用組合數計數分配一下
// ?? 還沒處理吧,所以??可以在這個層次上再套一層dp
// val[i][w] 表示考慮到字母i已經用了w個??的合法方案數
for(int j = 1; j <= 10; ++j) {
if(cnt[i][j] < need[i][j]) {
flag = 0;
break;
}
fdp[i] = fdp[i] * COMB(cnt[i][j], need[i][j]);
// for(int k = 0; k <= need[i][0]; ++k) rdp[j][k] = rdp[j - 1][k];
if(1 || cnt[i][j] > need[i][j] && need[i][j] > 0) {
for(int h = 0; h <= cnt[i][j] - need[i][j]; ++h) {
for(int k = h; k <= need[i][0]; ++k) {
rdp[j][k] = rdp[j][k] + rdp[j - 1][k - h] * COMB(cnt[i][j], need[i][j] + h);
// debug(h, j, k, rdp[j][k])
}
}
// if(i == 'C' - 'A') debug(j)
}else {
// if(i == 'C' - 'A') debug(j, rdp[j][1], rdp[j - 1][1])
}
}
// rdp[10][0] = 0;
fdp[i] = fdp[i] * COMB(sum[0][i] - (sum[1][i] - need[i][0]), need[i][0]);
LL ret = rdp[10][need[i][0]];
if(i == 'C' - 'A') debug(i, w, need[i][0], fdp[i], rdp[10][need[i][0]], fdp[i] - rdp[10][need[i][0]], ret, fdp[i] - ret)
res *= (ret);
need[i][0] -= w;
val[i][w] = ret;
if(i == 'C' - 'A' && val[i][w] > 1) debug(i, w, val[i][w])
}
}
for(int j = 0; j <= ww; ++j) dp[0][j] = val[0][j];
for(int i = 1; i < 26; ++i) {
for(int j = 0; j <= ww; ++j) {
for(int k = 0; k <= j; ++k) {
dp[i][j] = dp[i][j] + dp[i - 1][j - k] * val[i][k];
}
}
}
res = dp[25][ww];
LL allcase = COMB(n, m);
LL agcd = __gcd(allcase, res);
debug(allcase, res, res / agcd, allcase / agcd)
if(flag == 0) {
printf("0/1\n");
}else {
printf("%lld/%lld\n", res / agcd, allcase / agcd);
}
// 不確定有沒有爆 long long
for(int i = 0; i < 26; ++i) {
sum[0][i] = sum[1][i] = 0;
for(int j = 0; j <= 10; ++j) {
cnt[i][j] = need[i][j] = 0;
}
for(int j = 0; j <= ww; ++j) {
val[i][j] = 0;
dp[i][j] = 0;
}
}
}
int main() {
#ifdef LH_LOCAL
freopen("c:/lh/system-lab/acm_code/OJ/in.txt", "r", stdin);
// freopen("c:/lh/system-lab/acm_code/OJ/out.txt", "w", stdout);
#endif
for(int cas = 1, tim = (1 ? read(): 1); cas <= tim; ++ cas) {
work();
}
// 56160000
debug(26*60*10*60*60)
#ifdef LH_LOCAL
cout << "Debug log: time cost:" << 1.0 * clock() / CLOCKS_PER_SEC << "s" << endl;
#endif
return 0;
}
/*
3
40 2
0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0S 1S 2S 3S 4S 5S 6S 7S 8S 9S 0P 1P 2P 3P 4P 5P 6P 7P 8P 9P
?C 1C
40 16
0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0S 1S 2S 3S 4S 5S 6S 7S 8S 9S 0P 1P 2P 3P 4P 5P 6P 7P 8P 9P
0C 0C 1C 2C ?C ?C ?C ?C 3S 4S ?S ?S 5P 6P ?P ?P
60 30
1A 1B 1C 1D 1E 1F 1G 1H 1I 1J 1K 1L 1M 1N 1O 1P 1Q 1R 1S 1T 1U 1V 1W 1X 1Y 1Z 0A 1A 2A 3A 4A 5A 6A 7A 8A 9A 1S 3S 5S 7S 9S 0U 2U 4U 6U 8U 2Z 3Z 5Z 7Z 1C 1C 1C 1C 1C 1C 1C 1C 1S 1P
1C 1C 1S 1P 2A 0A 1A 9A ?S ?U ?Z ?H ?O ?U ?? ?? ?? ?? ?? ?? ?S ?S ?S ?U ?U ?U ?Z ?Z ?C ?C
37/780
167384/2417388525 -> 4351984/x -> 5551 * 28 * 28
50442363273/29566145391215356 -> 201769453092/x
Sample
Input
15
6 1
0C 0S 0P 1C 1S 1P
1S
6 1
0C 0S 0P 1C 1S 1P
?C
6 2
0C 0S 0P 1C 1S 1P
?C ?C
6 2
0C 0S 0P 1C 1S 1P
?C ?S
6 4
0C 0S 0P 1C 1S 1P
?C ?C ?S ?P
6 4
0C 0S 0P 1C 1S 1P
0C ?C ?S ?P
6 4
0C 0S 0P 1C 1S 1P
?C ?C ?S ??
6 4
0C 0S 0P 1C 1S 1P
?C ?C ?? ??
6 4
0C 0S 0P 1C 1S 1P
?A ?? ?? ??
6 4
0C 0S 0P 1C 1S 1P
0C 0C ?S ?P
30 8
0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0S 1S 2S 3S 4S 5S 6S 7S 8S 9S 0P 1P 2P 3P 4P 5P 6P 7P 8P 9P
0C ?C ?C ?C 1S ?S 2P ?P
40 2
0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0S 1S 2S 3S 4S 5S 6S 7S 8S 9S 0P 1P 2P 3P 4P 5P 6P 7P 8P 9P
?C 1C
40 2
0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0S 1S 2S 3S 4S 5S 6S 7S 8S 9S 0P 1P 2P 3P 4P 5P 6P 7P 8P 9P
?C 1S
40 16
0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0S 1S 2S 3S 4S 5S 6S 7S 8S 9S 0P 1P 2P 3P 4P 5P 6P 7P 8P 9P
0C 0C 1C 2C ?C ?C ?C ?C 3S 4S ?S ?S 5P 6P ?P ?P
60 30
1A 1B 1C 1D 1E 1F 1G 1H 1I 1J 1K 1L 1M 1N 1O 1P 1Q 1R 1S 1T 1U 1V 1W 1X 1Y 1Z 0A 1A 2A 3A 4A 5A 6A 7A 8A 9A 1S 3S 5S 7S 9S 0U 2U 4U 6U 8U 2Z 3Z 5Z 7Z 1C 1C 1C 1C 1C 1C 1C 1C 1S 1P
1C 1C 1S 1P 2A 0A 1A 9A ?S ?U ?Z ?H ?O ?U ?? ?? ?? ?? ?? ?? ?S ?S ?S ?U ?U ?U ?Z ?Z ?C ?C
Output
1/6
1/3
1/15
4/15
4/15
4/15
1/3
2/5
0/1
0/1
252/216775
37/780
1/39
167384/2417388525
50442363273/29566145391215356
Hint
對于分數 a/b,設 g=gcd(a,b),那么其最簡分數形式為 (a/g)/(b/g),
*/
ACMer,無怨無悔
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/554779.html
標籤:其他
下一篇:返回列表
