总结
期望得分:\(30 + 30 + 0\)
实际得分:\(30 + 0 + 0\)
炸了,这次考试完全炸了,\(T1\)只会打暴力,\(T2\)神奇大\(DP\),\(T3\)概率期望迷
\(T1\)想不出\(70\)分来,只能默默\(orz\ cgp\)大佬
\(T2\)打错了……\(awsl\)
\(T3\)是个鬼……\(毒\)不懂
思路
T1
先打\(30\)分暴力,这个很好打
然后\(70\)分\(O(n\log^2)\)怎么写呢
考虑二分答案
二分的左端点为\(0\),右端点为整整两个序列中的最大值\(maxn\),\(len\)先计算出取出的区间长度的中间值,然后进行二分,用\(upper\_bound\)计算出\(A\)序列中在\(mid\)左边的数的个数\(now1\),以及\(B\)序列中在\(mid\)左边的数的个数\(now2\),若他们两个的长度大于\(len\)就缩小范围到\(mid\)左边,相应的缩小\(r1\)和\(r2\)的范围,反之则缩小范围到\(mid\)右边,相应扩大\(l1\)和\(l2\),最后输出\(ans\)就好了
满分咋做?(\(from\ solution\))
实际上我们可以进行一波分类讨论。可以通过当前数的位置得到
这个数在另一个序列的期望位置。假设当前的数为\(x\),期望位置的数为 \(y\),下一个数为\(z\),那么 \(z\le x \le y\)时\(x\)就是答案,否则比较一下大小,往两边跳。
这种方法要特判很多种情况。事实上,我们还有一种较为简便,
普适性更强的方法。假设当前要取的是区间的第\(k\)大,将\(k\)折半,放在两个区间的对应位置 \(s, t\)上,比较 \(a[s], b[t]\),不妨设\(a[s] < b[t]\),那么答案可以化归至区间\([l1, s - 1],[l2, r2]\)的第\(\frac{k}{2}\)大数(因为\(a\)序列比\(a[s]\)小的那些数一定可以全部舍去), 递归即可
T2
\(30\)分的话,可以想到一个\(n^2\)的\(dp\)方程
用\(dp[i]\)表示分割\([l,i]\)的最大答案,转移方程为
\[dp[i] = \max_{j = 0}^{i - 1}dp[j] + f(\min_{x = j + 1}^{i} a_x),dp[0] = 0\]
满分做法?(同样\(from\ solution\))
显然可以采用一个单调递增的栈来维护\(g_x = \min_{x = j}^{i}a[x]\)具体地,单调栈中的元素\(l_1,l_2,…l_m\)表示\(g_{l_i}!=g_{l_{i-1}}\)的每个\(l_i\)(就是最小值变化的转折点),那么有\(\forall x \in [l_i, l_{i+1} -1], g(x)\)相同,此时\(dp\)值最大的那个点一定最优秀,于是维护\(h_i = \max_{x = l_i}^{l_i +1}dp[x]\),表示每个取到最小值元素对应区间的最优答案。这样的话,每一次的答案就是\(\max h_i + f(g_{l_i})\),采用一棵线段树或者可删除堆维护单调栈即可。
T3
不会,咕咕咕
代码
T1
考场三十分代码
/* By:Loceaner */ #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; inline int read() { char c = getchar(); int x = 0, f = 1; for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1; for( ; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + (c ^ 48); return x * f; } const int N = 5e5 + 11; int n, m, a[N], b[N], now[N], cnt, tot, ans[N]; int opt, x, y, z, l1, l2, r1, r2; void solve() { while(m--) { opt = read(); if(opt == 1) { x = read(), y = read(), z = read(); if(x == 0) a[y] = z; else b[y] = z; } else { l1 = read(), r1 = read(), l2 = read(), r2 = read(); cnt = 0; while(l1 <= r1 || l2 <= r2) { if(l1 <= r1 && l2 <= r2) { if(a[l1] < b[l2]) now[++cnt] = a[l1++]; else now[++cnt] = b[l2++]; } else if(l1 <= r1) now[++cnt] = a[l1++]; else now[++cnt] = b[l2++]; } // cout << "now: "; // for(int i = 1; i <= cnt; i++) cout << now[i] << " "; // cout << '\n'; ans[++tot] = now[cnt / 2 + 1]; } } for(int i = 1; i < tot; i++) cout << ans[i] << '\n'; cout << ans[tot]; } int main() { freopen("median.in", "r", stdin); freopen("median.out", "w", stdout); n = read(), m = read(); for(int i = 1; i <= n; i++) a[i] = read(); for(int i = 1; i <= n; i++) b[i] = read(); if(n <= 1000 && m <= 2000) return solve(), 0; return 0; }
跑五秒才能过的正解代码
/* By:Loceaner */ #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; inline int read() { char c = getchar(); int x = 0, f = 1; for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1; for( ; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + (c ^ 48); return x * f; } const int N = 5e5 + 11; int n, m, a[N], b[N], now[N], cnt, tot, ans[N]; int opt, x, y, z, l1, l2, r1, r2; void solve() { while(m--) { opt = read(); if(opt == 1) { x = read(), y = read(), z = read(); if(x == 0) a[y] = z; else b[y] = z; } else { l1 = read(), r1 = read(), l2 = read(), r2 = read(); cnt = 0; while(l1 <= r1 || l2 <= r2) { if(l1 <= r1 && l2 <= r2) { if(a[l1] < b[l2]) now[++cnt] = a[l1++]; else now[++cnt] = b[l2++]; } else if(l1 <= r1) now[++cnt] = a[l1++]; else now[++cnt] = b[l2++]; } // cout << "now: "; // for(int i = 1; i <= cnt; i++) cout << now[i] << " "; // cout << '\n'; ans[++tot] = now[cnt / 2 + 1]; } } for(int i = 1; i < tot; i++) cout << ans[i] << '\n'; cout << ans[tot]; } int main() { freopen("median.in", "r", stdin); freopen("median.out", "w", stdout); n = read(), m = read(); int maxn = 0; for(int i = 1; i <= n; i++) a[i] = read(), maxn = max(maxn, a[i]); for(int i = 1; i <= n; i++) b[i] = read(), maxn = max(maxn, a[i]); if(n <= 1000 && m <= 2000) return solve(), 0; while(m--) { opt = read(); if(opt == 1) { x = read(), y = read(), z = read(); if(!x) a[y] = z; else b[y] = z; maxn = max(maxn, z); } else if(opt == 2) { l1 = read(), r1 = read(), l2 = read(), r2 = read(); int l = 0, r = maxn, len = (r1 - l1 + 1 + r2 - l2 + 1) / 2 + 1, ans; while(l <= r) { int mid = (l + r) >> 1; int now1 = upper_bound(a + l1, a + r1 + 1, mid) - (a + l1); int now2 = upper_bound(b + l2, b + r2 + 1, mid) - (b + l2); if(now1 + now2 >= len) { ans = mid; r = mid - 1; r1 = l1 + now1 - 1; r2 = l2 + now2 - 1; } else { l = mid + 1; len -= now1 + now2; l1 = l1 + now1; l2 = l2 + now2; } } cout << ans << '\n'; } } return 0; }
正解\(1\)(要九九八,不要九十八,只需一点五,AC带回家)
#include<cstdio> #include<algorithm> const int N = 5e5 + 10; int ri() { char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1; for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f; } int a[N], b[N]; int Cal(int l, int r, int *a, int st, int ed, int *b) { int p = r - l + 1 + ed - st + 1 >> 1, L = l, R = r; for(;L <= R;) { int m = L + R >> 1, c = p - (m - l + 1) + st; if(c == st - 1 && m - l == p && a[m] <= b[st]) return a[m]; if(c < st) {R = m - 1; continue;} else if(c > ed) {L = m + 1; continue;} if(a[m] >= b[c] && (a[m] <= b[c + 1] || c == ed)) return a[m]; if(a[m] >= b[c]) R = m - 1; else L = m + 1; } return 0; } int main() { freopen("median.in","r",stdin); freopen("median.out","w",stdout); int n = ri(), m = ri(); for(int i = 1;i <= n; ++i) a[i] = ri(); for(int i = 1;i <= n; ++i) b[i] = ri(); for(;m--;) { int op = ri(); if(op == 1) { int w = ri(), x = ri(), y = ri(); !w ? a[x] = y : b[x] = y; } else { int l = ri(), r = ri(), l1 = ri(), r1 = ri(), x; if(x = Cal(l, r, a, l1, r1, b)) printf("%d\n", x); else printf("%d\n", Cal(l1, r1, b, l, r, a)); } } return 0; } /* 6 1 1 2 4 5 6 7 1 1 3 5 6 7 2 1 6 1 1 */
正解\(2\)
#include <bits/stdc++.h> int ri() { char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1; for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f; } const int N = 5e5+50; int a[N], b[N]; int kth(int ta[], int sa, int tb[], int sb, int k) { if (sa > sb) return kth(tb, sb, ta, sa, k); if (sa == 0) return tb[k]; if (k == 1) return std::min(ta[1], tb[1]); int ka = std::min(sa, k/2), kb = k - ka; if (ta[ka] < tb[kb]) return kth(ta+ka, sa-ka, tb, sb, k-ka); return kth(ta, sa, tb+kb, sb-kb, k-kb); } int query(int la, int ra, int lb, int rb) { int sa = ra-la+1, sb = rb-lb+1, siz = sa + sb; return kth(a+la-1, sa, b+lb-1, sb, siz/2+1); } int main() { freopen("median.in", "r", stdin); freopen("median.out", "w", stdout); int n, m; n = ri(); m = ri(); for (int i = 1; i <= n; i++) a[i] = ri(); for (int i = 1; i <= n; i++) b[i] = ri(); for (int opt; m--; ) { opt = ri(); if (opt == 2) { int la = ri(), ra = ri(), lb = ri(), rb = ri(); printf("%d\n", query(la, ra, lb, rb)); } else { int p = ri(), pos = ri(), val = ri(); if (p == 0) a[pos] = val; else b[pos] = val; } } return 0; } /* 5 5 12 41 46 68 69 35 61 82 84 96 2 1 4 3 5 1 0 5 75 2 2 4 3 4 2 3 4 1 5 2 1 4 2 4 */
T2
考场爆零代码
/* By:Loceaner */ #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #define ll long long using namespace std; inline int read() { char c = getchar(); int x = 0, f = 1; for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1; for( ; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + (c ^ 48); return x * f; } const int N = 2e5 + 11; int n, A, B, C, D; int a[N]; ll f[N], dp[N]; ll calc(int x) { return 1LL * A * x * x * x + B * x * x + C * x + D; } int minn[1011][1011]; inline int query(int l, int r) { int k = log2(r - l + 1); return min(minn[l][k], minn[r - (1 << k) + 1][k]); } void solve1() { memset(dp, -0x3f, sizeof(dp)); dp[0] = 0; for(int i = 1; i <= n; i++) { for(int j = 1; j <= i; j++) { dp[i] = max(dp[i], dp[j - 1] + calc(query(j, i))); } } cout << dp[n] << '\n'; return; } void solve2() { memset(dp, -0x3f, sizeof(dp)); dp[0] = 0; for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { dp[i] = max(dp[i], dp[j - 1] + calc(minn[j][i])); } } cout << dp[n] << '\n'; return; } int main() { freopen("min.in", "r", stdin); freopen("min.out", "w", stdout); n = read(), A = read(), B = read(), C = read(), D = read(); for(int i = 1; i <= n; i++) a[i] = read(), f[i] = calc(a[i]), minn[i][0] = a[i]; for(int j = 1; (1 << j) <= n; j++) for(int i = 1; i <= n; i++) minn[i][j] = min(minn[i][j - 1], minn[i + (1 << (j - 1))][j - 1]); // for(int i = 1; i <= n; i++) cout << f[i] << ' '; if(n <= 1000) return solve1(), 0; if(A == 0 && B == 0 && C <= 0) return solve2(), 0; else for(int i = 1;i <= n; ++i) if(fabs(calc(a[i])) >= 1e12) {return printf("%d\n", a[i]), 0;}; return 0; }
正解
#include<cstdio> #include<cstring> #include<algorithm> const int Nt = 524287; const long long inf = 0x3f3f3f3f3f3f3f3f; int ri() { char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1; for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f; } int a[Nt], st[Nt], tp, n, A, B, C, D; long long T[Nt << 1], f[Nt], mx[Nt]; void Up(int i, long long x) {for(T[i += Nt] = x; i >>= 1;) T[i] = std::max(T[i << 1], T[i << 1 | 1]);} long long Cal(long long x) {return ((A * x + B) * x + C) * x + D;} int main() { freopen("min.in","r",stdin); freopen("min.out","w",stdout); n = ri(); A = ri(); B = ri(); C = ri(); D = ri(); for(int i = 1;i <= n; ++i) a[i] = ri(); std::memset(T, -0x3f, sizeof(T)); f[0] = 0; mx[1] = 0; st[tp = 1] = a[1]; Up(1, Cal(a[1])); for(int i = 1;i <= n; ++i) { f[i] = T[1]; long long x = f[i]; for(;st[tp] > a[i + 1] && tp;) x = std::max(x, mx[tp]), Up(tp--, -inf); st[++tp] = a[i + 1]; mx[tp] = x; Up(tp, x + Cal(st[tp])); } printf("%lld\n", f[n]); return 0; }
T3
神仙正解
#include<cstdio> #include<algorithm> #define ls p << 1 #define rs p << 1 | 1 #define rt 1, 1, Q const int N = 1e5 + 10, Y = 2e5 + 10, P = 1e9 + 7; int ri() { char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1; for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f; } int t[N << 2], tm[N << 2], c[Y], b[Y], l[Y], r[Y], pr[Y], to[Y << 1], nx[Y << 1], Q, n, m, k, H; void add(int x, int p) {to[++H] = 1LL * to[pr[x]] * (1 - p) % P; nx[H] = pr[x]; pr[x] = H;} struct D {int l, r;} q[N]; struct X {int x, y, p;}p[Y]; bool cmp1(X a, X b) {return a.y < b.y;} bool cmp2(D a, D b) {return a.l == b.l ? a.r < b.r : a.l < b.l;} int Pow(int x, int k) { int r = 1; for(;k;x = 1LL * x * x % P, k >>= 1) if(k & 1) r = 1LL * r * x % P; return r; } void B(int p, int L, int R) { tm[p] = 1; if(L == R) return void(t[p] = 1); int m = L + R >> 1; B(ls, L, m); B(rs, m + 1, R); t[p] = t[ls] + t[rs]; } void Tag(int p, int v) {tm[p] = 1LL * tm[p] * v % P; t[p] = 1LL * t[p] * v % P;} void Pd(int p) {if(tm[p] != 1) Tag(ls, tm[p]), Tag(rs, tm[p]), tm[p] = 1;} void M(int p, int L, int R, int st, int ed, int v) { if(L == st && ed == R) return Tag(p, v); int m = L + R >> 1; Pd(p); if(st <= m) M(ls, L, m, st, std::min(ed, m), v); if(ed > m) M(rs, m + 1, R, std::max(st, m + 1), ed, v); t[p] = (t[ls] + t[rs]) % P; } void C(int x) { if(l[x] > r[x]) return ; int m = Pow(1 - (to[pr[x]] - b[x]) % P, P - 2); pr[x] = nx[pr[x]]; M(rt, l[x], r[x], 1LL * (1 - (to[pr[x]] - b[x]) % P) * m % P); } int main() { freopen("max.in","r",stdin); freopen("max.out","w",stdout); n = ri(); m = ri(); Q = ri(); int tp = 0; for(int i = 1;i <= m; ++i) { int x = ri(), y = ri(), px = ri(); if(!px || !y) continue; p[++tp].x = x; p[tp].y = y; p[tp].p = px; } std::sort(p + 1, p + tp + 1, cmp1); for(int i = 1;i <= n; ++i) to[++H] = 1, pr[i] = H; for(int i = 1;i <= tp; ++i) add(p[i].x, p[i].p); for(int i = 1;i <= n; ++i) b[i] = to[pr[i]]; for(int i = 1;i <= Q; ++i) q[i].l = ri(), q[i].r = ri(); std::sort(q + 1, q + Q + 1, cmp2); int L = 1, R = 0; for(int i = 1;i <= n; ++i) { for(;L <= R && q[L].r < i; ++L) ; for(;q[R + 1].l <= i && R < Q; ++R) ; l[i] = L; r[i] = R; } B(rt); int A = 0; p[0].y = 0; for(int i = tp, j; i; i = j) { for(j = i;p[j].y == p[i].y && j; --j) C(p[j].x); A = (A + 1LL * t[1] * (p[i].y - p[j].y)) % P; } A = (1LL * p[tp].y * Q - A) % P; printf("%d\n", (A + P) % P); return 0; }