\(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; }