2021牛客暑期多校训练营5
Boxes
如果用hint,必定在开局使用,因为信息没有衰减,早用一定比晚用好。也就是说,我先hint出来,那我一边开盒子一边算,一样可以达到后面再开的效果。
那么考虑每一种分布,分别统计期望即可
#include <bits/stdc++.h> #define sc(x) scanf("%lld", &(x)) #define pr(x) printf("%lld\n", (x)) #define rep(i, l, r) for (int i = l; i <= r; ++i) using namespace std; typedef long long ll; typedef long double ld; const int N = 1e5 + 7; const int mod = 1e9 + 7; ld sum[N], a[N], c; signed main() { int n; ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); cin >> n >> c; rep(i, 1, n) cin >> a[i]; sort(a + 1, a + n + 1); rep(i, 1, n) sum[i] = a[i] + sum[i - 1]; ld ans = c; rep(i, 1, n-1) ans += sum[i] / pow(2.0, n - i); ans = min(ans, sum[n]); cout << fixed << setprecision(10) << ans; return 0; }
Double Strings
用dp转移相同子序列数量,用组合数计算两串选出等长串,能选多少种
#include <bits/stdc++.h> #define sc(x) scanf("%lld", &(x)) #define pr(x) printf("%lld\n", (x)) #define rep(i, l, r) for (int i = l; i <= r; ++i) using namespace std; typedef long long ll; const int N = 5007; const int mod = 1e9 + 7; int fac[N * 2] = {1, 1}, inv[N * 2] = {1, 1}; int dp[N][N]; // dp[i][j]表示 在A串前i个字符 B串前j个字符 有多少子序列相同 char a[N], b[N]; inline void init() { for (int i = 2; i < N * 2; ++i) { fac[i] = (ll)fac[i - 1] * i % mod; inv[i] = (ll)(mod - mod / i) % mod * inv[mod % i] % mod; } for (int i = 1; i < N * 2; ++i) inv[i] = (ll)inv[i] * inv[i - 1] % mod; } inline int C(int n, int m) { if (n < m) return 0; if (n == m) return 1; return (ll)fac[n] * inv[m] % mod * inv[n - m] % mod; } signed main() { init(); scanf("%s%s", a + 1, b + 1); int n = strlen(a + 1), m = strlen(b + 1); rep(i, 1, n) rep(j, 1, m) { dp[i][j] = (dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1]) % mod; // 考虑不在两串中同时加入本字符的情况,那么就要么从a串尾缀,要么从b串尾缀,容斥处理一下即可 if (a[i] == b[j]) dp[i][j] = (dp[i][j] + dp[i - 1][j - 1] + 1) % mod; // 如果a[i] == b[j] 即考虑同时在两串中加入本字符的情况 // dp[i][j]可以自dp[i-1][j-1]转移(即尾缀本字符) // 也可以重新开始(+1) 即从本位开始 if (dp[i][j] < 0) dp[i][j] += mod; } ll ans = 0; rep(i, 1, n) rep(j, 1, m) if (b[j] > a[i]) // 前面相同(加一个空串),后面随便选 ans = (ans + (ll)(dp[i - 1][j - 1] + 1) * C(n - i + m - j, n - i)) % mod; pr(ans); return 0; }
Holding Two
签到构造
#include <bits/stdc++.h> #define sc(x) scanf("%lld", &(x)) #define pr(x) printf("%lld\n", (x)) #define rep(i, l, r) for (int i = l; i <= r; ++i) using namespace std; typedef long long ll; const int N = 1e3 + 7; const int mod = 1e9 + 7; char a[N][N]; int n, m; bool chk(int x, int y) { for (int i = 1; i * 2 + x <= n; ++i) for (int j = 1; j * 2 + y <= m; ++j) { if (a[x][y] == a[x + i][y + j] and a[x][y] == a[i * 2 + x][j * 2 + y]) { cerr << x << ' ' << y << ' ' << x + i << ' ' << y + j << endl; return 0; } } return 1; } int main() { cin >> n >> m; rep(i, 1, n) rep(j, 1, m) { if (((i - 1) / 2) % 2 == 0) a[i][j] = (j & 1) + '0'; else a[i][j] = '0' + !(j & 1); } rep(i, 1, n) puts(a[i] + 1); // rep(i, 1, n) rep(j, 1, m) { // if (!chk(i, j)) cout << i << ' ' << j << '\n'; // } return 0; } /* 10101010 10101010 01010101 01010101 */
King of Range
题意是求满足极值大于k的区间有多少个。
思路是st表预处理区间极大值和极小值,然后跑双指针
#include <bits/stdc++.h> #define sc(x) scanf("%lld", &(x)) #define pr(x) printf("%lld\n", (x)) #define rep(i, l, r) for (int i = l; i <= r; ++i) using namespace std; typedef long long ll; const int N = 1e5 + 7; const int mod = 1e9 + 7; int a[N], f[N][35], g[N][35], pw[35], lg[N]; inline int query(int l, int r) { int k = lg[r - l + 1]; return max(f[l][k], f[r - pw[k] + 1][k]) - min(g[l][k], g[r - pw[k] + 1][k]); } signed main() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); pw[0] = 1; for (int i = 1; i <= 30; ++i) pw[i] = pw[i - 1] * 2; memset(g, 0x3f, sizeof g); int n, m, k, t; cin >> n >> m; rep(i, 1, n) { cin >> a[i]; f[i][0] = g[i][0] = a[i]; lg[i] = log2(i); } t = lg[n] + 1; for (int j = 1, tot; j < t; ++j) { tot = n - pw[j] + 1; for (int i = 1; i <= tot; ++i) { f[i][j] = max(f[i][j - 1], f[i + pw[j - 1]][j - 1]); g[i][j] = min(g[i][j - 1], g[i + pw[j - 1]][j - 1]); } } while (m--) { ll ans(0); cin >> k; for (int R = 1, L = 1; R <= n; ++R) { // 对于每个R while (L <= R && query(L, R) > k) ++L; ans += L - 1; // 1到L-1都满足 } cout << ans << '\n'; } return 0; }