\(dp[i][j]\) 表示已有 \(i\) 個 \(a\) 和 \(j\) 個 \(ab\) 的狀況下繼續構造能獲得的 \(ab\) 個數的指望。c++
考慮 DFS 記憶化搜索。spa
有兩個要注意的地方:
令 \(p_a\) 爲添加 \(a\) 的機率,\(p_b\) 爲添加 \(b\) 的機率。code
#include<bits/stdc++.h> using namespace std; const int MOD = 1e9 + 7; long long POW(long long x, int k) { long long ret = 1; while(k) { if(k & 1) ret = (ret * x) % MOD; x = x * x % MOD; k >>= 1; } return ret; } long long k, pa, pb, dp[1001][1001]; long long dfs(int a, int ab) { if(a + ab >= k) return a + ab + pa * POW(pb, MOD - 2) % MOD; if(dp[a][ab] != -1) return dp[a][ab]; return dp[a][ab] = (dfs(a + 1, ab) * pa % MOD + dfs(a, ab + a) * pb % MOD) * POW(pa + pb, MOD - 2) % MOD; } int main() { memset(dp, -1, sizeof dp); cin >> k >> pa >> pb; long long p = __gcd(pa, pb); pa /= p; pb /= p; cout << dfs(1, 0) << endl; return 0; }
官方題解 很詳細了,本身補充幾點。blog
#include<bits/stdc++.h> using namespace std; const int N = 1e3 + 10; const int MOD = 1e9 + 7; long long C[N][N]; map<long long, int> mp; long long a[N], b[N], c[N][N]; int main() { for(int i = 0; i < N; i++) { C[i][0] = 1; for (int j = 1; j <= i; j++) { C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD; } } int n, m; cin >> m >> n; while(n--) { long long y = 0; for (int i = 0; i < m; i++) { long long x; scanf("%1lld", &x); a[i] |= x << n; } } for (int i = 0; i < m; i++) { mp[a[i]]++; } b[0] = b[1] = 1; for (int i = 2; i <= m; i++) { for (int j = 0; j < i; j++) { (b[i] += C[i - 1][j] * b[i - j - 1]) %= MOD; } } long long ans = 1; for(auto it : mp) { (ans *= b[it.second]) %= MOD; } cout << ans << endl; return 0; }
以 G 爲間隔分組,每一個組內單獨討論,要知足題目條件有兩種連法:ip
#include<bits/stdc++.h> using namespace std; int preg, prer, preb, fr, lr, fb, lb, mxr, mxb; int main() { int n; cin >> n; long long ans = 0; for (int i = 0; i < n; i++) { int p; char c[2]; scanf("%d%s", &p, c); if(c[0] == 'G') { if(prer) mxr = max(mxr, p - prer); if(preb) mxb = max(mxb, p - preb); if(!preg) { // 第一個 G 前面 if(fr) { ans += p - fr; } if(fb) { ans += p - fb; } } else { // 兩個 G 之間 ans += min(2LL * (p - preg), 3LL * (p - preg) - mxb - mxr); } prer = p; preb = p; preg = p; lb = lr = p; mxb = mxr = 0; } else if(c[0] == 'B') { if(!fb) fb = p; if(preb) mxb = max(mxb, p - preb); preb = p; lb = p; } else if(c[0] == 'R') { if(!fr) fr = p; if(prer) mxr = max(mxr, p - prer); prer = p; lr = p; } } if(!preg) { if(lr != fr) ans += lr - fr; if(lb != fb) ans += lb - fb; } else { ans += lr - preg; ans += lb - preg; } cout << ans << endl; return 0; }
\(dp[i][j][k][o]\) 表示前 \(i\) 個數中有 \(j\) 個大於等於 \(k\) 的數時的構數方案數,\(o\) 爲了保證邊界,構造的數不大於 \(X\)。ci
舉個例子,比方說一個數 \(3312\) 咱們要累積這個數對答案的貢獻,其實是 \(1233\) ,大於等於 \(1\) 的數有 \(4\) 個,咱們能夠認爲貢獻了 \(1111\) ,大於等於 \(2\) 的數有 \(3\) 個,貢獻了 \(111\) ,大於等於 \(3\) 的數有 \(2\) 個,貢獻了 \(11\) 。get
最後枚舉 \(k\),累計計算一下答案便可。it
#include<bits/stdc++.h> using namespace std; const int MOD = 1e9 + 7; const int N = 707; char s[N]; int dp[N][N][10][2]; int main() { scanf("%s", s + 1); int n = strlen(s + 1); for (int i = 1; i < 10; i++) { dp[0][0][i][0] = 1; } for (int i = 0; i < n; i++) { for (int j = 0; j <= i; j++) { for (int k = 1; k < 10; k++) { for (int o = 0; o < 2; o++) { for (int p = 0; p <= (!o ? s[i + 1] - '0' : 9); p++) { (dp[i + 1][j + (p >= k)][k][o | p < s[i + 1] - '0'] += dp[i][j][k][o]) %= MOD; } } } } } long long ans = 0; for(int k = 1; k < 10; k++) { for (long long i = 1, p = 1; i <= n; i++, p = (p * 10 + 1) % MOD) { (ans += p * (dp[n][i][k][0] + dp[n][i][k][1]) % MOD) %= MOD; } } printf("%I64d\n", ans); return 0; }