【CodeForces】CodeForces Round #465 (Div. 2) 题解
2018-02-23 20:13
661 查看
【比赛链接】
点击打开链接
【题解链接】
点击打开链接
【A】Fafa and his Company
【思路要点】
求\(N\)的因数个数减1,乱做就行。
时间复杂度\(O(N)\)或\(O(\sqrt{N})\)
【代码】
【B】Fafa and the Gates
【思路要点】
按照题意模拟过程即可。
时间复杂度\(O(N)\)。
【代码】
【C】Fifa and Fafa
【思路要点】
将圆心设置在两个点的连线上,使得圆经过\((x_2,y_2)\),包含\((x_1,y_1)\),且与大圆相切。
注意特判点在圆外和两点重合的情况。
时间复杂度\(O(1)\)。
【代码】
【D】Fafa and Ancient Alphabet
【思路要点】
逐位确定大小关系,将\(S_1\)大于\(S_2\)的概率加入答案,保留\(S_1\)等于\(S_2\)的概率到下一位。
时间复杂度\(O(N)\)。
【代码】
【E】Fafa and Ancient Mathematics
【思路要点】
我们可以将问题转化成如下的形式:有一棵二叉树,它的叶子节点上是数,非叶节点上需要填入一个符号,使得其先下后上地计算得到的值最大化。
考虑DP,记录\(Max_{i,j}\)和\(Min_{i,j}\)表示在第\(i\)个节点子树中填入\(j\)个加号/减号,其他的填减号/加号,可以得到的最大/最小得数。
转移较为显然,枚举根节点的符号以及左右子树符号的分配即可。
状态的第二维记录输入时较少的一种符号。
时间复杂度\(O(|E|*Min^2(P,M))\)。
【代码】
【F】Fafa and Array
【思路要点】
令当前数组相邻值的差的绝对值之和为\(sum\),对于询问操作,答案不会超过\(sum+2*x\)。
当且仅当区间中存在一个大于等于左右两边的数的数,答案等于\(sum+2*x\)。
否则,区间一定是由至多两段递增或递减的数组成的,我们找到断点,分别处理断点和两侧。
若询问在一段递增或递减的数上,那么我们应该操作在相差最小的一对相邻的数上。
将数组差分,用线段树维护区间最大、最小值,以及一些有关断点的信息,实现上述过程。
本题笔者的做法实现较为困难,代码可读性较低。
时间复杂度\(O(QLogN)\)。
【代码】
点击打开链接
【题解链接】
点击打开链接
【A】Fafa and his Company
【思路要点】
求\(N\)的因数个数减1,乱做就行。
时间复杂度\(O(N)\)或\(O(\sqrt{N})\)
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 5005; template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int main() { int n, ans = 0; read(n); for (int i = 1; i <= n - 1; i++) if (n % i == 0) ans++; printf("%d\n", ans); return 0; }
【B】Fafa and the Gates
【思路要点】
按照题意模拟过程即可。
时间复杂度\(O(N)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } char s[MAXN]; int main() { int n; read(n); scanf("%s", s + 1); int x = 0, y = 0, ans = 0; for (int i = 1; i <= n; i++) { if (x == y && s[i] == s[i - 1]) ans++; if (s[i] == 'U') y++; else x++; } cout << ans << endl; return 0; }
【C】Fifa and Fafa
【思路要点】
将圆心设置在两个点的连线上,使得圆经过\((x_2,y_2)\),包含\((x_1,y_1)\),且与大圆相切。
注意特判点在圆外和两点重合的情况。
时间复杂度\(O(1)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 5005; const long double eps = 1e-10; template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int main() { long double R, X1, Y1, X2, Y2; read(R), read(X1), read(Y1), read(X2), read(Y2); long double dist = sqrt((X1 - X2) * (X1 - X2) + (Y1 - Y2) * (Y1 - Y2)); if (dist >= R - eps) { printf("%.10Lf %.10Lf %.10Lf\n", X1, Y1, R - eps); return 0; } if (dist <= eps) { printf("%.10Lf %.10Lf %.10Lf\n", X1 + R / 2, Y1, R / 2 - eps); return 0; } long double ans = (R + dist) / 2 - eps, dx, dy; dx = X1 - X2, dy = Y1 - Y2; printf("%.10Lf %.10Lf %.10Lf\n", X2 + dx / dist * (dist + R) / 2, Y2 + dy / dist * (dist + R) / 2, ans); return 0; }
【D】Fafa and Ancient Alphabet
【思路要点】
逐位确定大小关系,将\(S_1\)大于\(S_2\)的概率加入答案,保留\(S_1\)等于\(S_2\)的概率到下一位。
时间复杂度\(O(N)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; const int P = 1e9 + 7; const int inv2 = (P + 1) / 2; template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int a[MAXN], b[MAXN]; long long power(long long x, long long y) { if (y == 0) return 1; long long tmp = power(x, y / 2); if (y % 2 == 0) return tmp * tmp % P; else return tmp * tmp % P * x % P; } int main() { int n, m; read(n), read(m); for (int i = 1; i <= n; i++) read(a[i]); for (int i = 1; i <= n; i++) read(b[i]); long long ans = 0, now = 1, inv = power(m, P - 2); for (int i = 1; i <= n; i++) { if (a[i] != 0 && b[i] != 0) { if (a[i] > b[i]) { ans += now; ans %= P; break; } if (a[i] < b[i]) break; continue; } now = now * inv % P; if (a[i] != 0 && b[i] == 0) { ans += now * (a[i] - 1); ans %= P; continue; } if (a[i] == 0 && b[i] != 0) { ans += now * (m - b[i]); ans %= P; continue; } ans += now * (m - 1) % P * inv2; ans %= P; } cout << ans << endl; return 0; }
【E】Fafa and Ancient Mathematics
【思路要点】
我们可以将问题转化成如下的形式:有一棵二叉树,它的叶子节点上是数,非叶节点上需要填入一个符号,使得其先下后上地计算得到的值最大化。
考虑DP,记录\(Max_{i,j}\)和\(Min_{i,j}\)表示在第\(i\)个节点子树中填入\(j\)个加号/减号,其他的填减号/加号,可以得到的最大/最小得数。
转移较为显然,枚举根节点的符号以及左右子树符号的分配即可。
状态的第二维记录输入时较少的一种符号。
时间复杂度\(O(|E|*Min^2(P,M))\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 10005; const int MAXM = 105; const int INF = 1e7; template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } char s[MAXN]; int size, p, m; int sum[MAXN], lc[MAXN], rc[MAXN]; int Max[MAXN][MAXM]; int Min[MAXN][MAXM]; int split(int l, int r) { int cnt = 0; for (int i = l; i <= r; i++) { if (s[i] == '(') cnt++; if (s[i] == ')') cnt--; if (s[i] == '?' && cnt == 0) return i; } return -1; } void chkmax(int &x, int y) {x = max(x, y); } void chkmin(int &x, int y) {x = min(x, y); } void workp(int pos, int l, int r) { if (l == r) { Max[pos][0] = Min[pos][0] = s[l] - '0'; return; } int mid = split(l + 1, r - 1); lc[pos] = ++size; rc[pos] = ++size; workp(lc[pos], l + 1, mid - 1); workp(rc[pos], mid + 1, r - 1); sum[pos] = 1 + sum[lc[pos]] + sum[rc[pos]]; for (int i = 0; i <= p && i <= sum[pos]; i++) { Max[pos][i] = -INF; Min[pos][i] = INF; for (int j = 0; j <= i && j <= sum[lc[pos]]; j++) { int k = i - j; if (k > sum[rc[pos]]) continue; chkmax(Max[pos][i], Max[lc[pos]][j] - Min[rc[pos]][k]); chkmin(Min[pos][i], Min[lc[pos]][j] - Max[rc[pos]][k]); } if (i == 0) continue; for (int j = 0; j <= i - 1 && j <= sum[lc[pos]]; j++) { int k = i - j - 1; if (k > sum[rc[pos]]) continue; chkmax(Max[pos][i], Max[lc[pos]][j] + Max[rc[pos]][k]); chkmin(Min[pos][i], Min[lc[pos]][j] + Min[rc[pos]][k]); } } } void workm(int pos, int l, int r) { if (l == r) { Max[pos][0] = Min[pos][0] = s[l] - '0'; return; } int mid = split(l + 1, r - 1); lc[pos] = ++size; rc[pos] = ++size; workm(lc[pos], l + 1, mid - 1); workm(rc[pos], mid + 1, r - 1); sum[pos] = 1 + sum[lc[pos]] + sum[rc[pos]]; for (int i = 0; i <= m && i <= sum[pos]; i++) { Max[pos][i] = -INF; Min[pos][i] = INF; for (int j = 0; j <= i && j <= sum[lc[pos]]; j++) { int k = i - j; if (k > sum[rc[pos]]) continue; chkmax(Max[pos][i], Max[lc[pos]][j] + Max[rc[pos]][k]); chkmin(Min[pos][i], Min[lc[pos]][j] + Min[rc[pos]][k]); } if (i == 0) continue; for (int j = 0; j <= i - 1 && j <= sum[lc[pos]]; j++) { int k = i - j - 1; if (k > sum[rc[pos]]) continue; chkmax(Max[pos][i], Max[lc[pos]][j] - Min[rc[pos]][k]); chkmin(Min[pos][i], Min[lc[pos]][j] - Max[rc[pos]][k]); } } } int main() { scanf("%s", s + 1); read(p), read(m); if (p <= m) { workp(++size, 1, strlen(s + 1)); printf("%d\n", Max[1][p]); } else { workm(++size, 1, strlen(s + 1)); printf("%d\n", Max[1][m]); } return 0; }
【F】Fafa and Array
【思路要点】
令当前数组相邻值的差的绝对值之和为\(sum\),对于询问操作,答案不会超过\(sum+2*x\)。
当且仅当区间中存在一个大于等于左右两边的数的数,答案等于\(sum+2*x\)。
否则,区间一定是由至多两段递增或递减的数组成的,我们找到断点,分别处理断点和两侧。
若询问在一段递增或递减的数上,那么我们应该操作在相差最小的一对相邻的数上。
将数组差分,用线段树维护区间最大、最小值,以及一些有关断点的信息,实现上述过程。
本题笔者的做法实现较为困难,代码可读性较低。
时间复杂度\(O(QLogN)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } struct info {int cnt, pos; }; info operator + (info a, info b) { info ans; ans.cnt = a.cnt + b.cnt; if (ans.cnt == 1) ans.pos = a.pos + b.pos; else if (a.pos) ans.pos = a.pos; else ans.pos = b.pos; return ans; } struct SegmentTree { struct Node{ int lc, rc; info top, low; long long Min, Max; } a[MAXN * 2]; int n, size, root; void update(int root) { int lc = a[root].lc, rc = a[root].rc; a[root].Min = min(a[lc].Min, a[rc].Min); a[root].Max = max(a[lc].Max, a[rc].Max); a[root].top = a[lc].top + a[rc].top; a[root].low = a[lc].low + a[rc].low; } void build(int &root, int l, int r, long long *val) { root = ++size; if (l == r) { a[root].Min = a[root].Max = val[l]; if (l != n) { bool tlow = val[l] <= 0 && val[l + 1] >= 0; a[root].low = (info) {tlow, l * tlow}; bool ttop = val[l] >= 0 && val[l + 1] <= 0; a[root].top = (info) {ttop, l * ttop}; } else { a[root].low = (info) {0, 0}; a[root].top = (info) {0, 0}; } return; } int mid = (l + r) / 2; build(a[root].lc, l, mid, val); build(a[root].rc, mid + 1, r, val); update(root); } void init(int x, long long *val) { n = x; root = size = 0; build(root, 1, n, val); } void modify(int root, int l, int r, int pos, long long val, bool ttop, bool tlow) { if (l == r) { a[root].Min = a[root].Max = val; a[root].top = (info) {ttop, l * ttop}; a[root].low = (info) {tlow, l * tlow}; return; } int mid = (l + r) / 2; if (mid >= pos) modify(a[root].lc, l, mid, pos, val, ttop, tlow); else modify(a[root].rc, mid + 1, r, pos, val, ttop, tlow); update(root); } long long queryval(int root, int l, int r, int pos) { if (l == r) return a[root].Max; int mid = (l + r) / 2; if (mid >= pos) return queryval(a[root].lc, l, mid, pos); else return queryval(a[root].rc, mid + 1, r, pos); } long long queryval(int pos) { return queryval(root, 1, n, pos); } void Modify(int pos, long long val) { if (pos != n) { long long tmp = queryval(root, 1, n, pos + 1); bool ttop = false, tlow = false; if (val <= 0 && tmp >= 0) tlow = true; if (val >= 0 && tmp <= 0) ttop = true; modify(root, 1, n, pos, val, ttop, tlow); } else modify(root, 1, n, pos, val, false, false); } void modify(int pos, long long val) { if (pos != n) { long long tmp = queryval(root, 1, n, pos + 1); bool ttop = false, tlow = false; if (val <= 0 && tmp >= 0) tlow = true; if (val >= 0 && tmp <= 0) ttop = true; modify(root, 1, n, pos, val, ttop, tlow); } else modify(root, 1, n, pos, val, false, false); if (pos != 1) Modify(pos - 1, queryval(root, 1, n, pos - 1)); } info querylow(int root, int l, int r, int ql, int qr) { if (l == ql && r == qr) return a[root].low; int mid = (l + r) / 2; info ans = (info) {0, 0}; if (mid >= ql) ans = ans + querylow(a[root].lc, l, mid, ql, min(qr, mid)); if (mid + 1 <= qr) ans = ans + querylow(a[root].rc, mid + 1, r, max(mid + 1, ql), qr); return ans; } info querylow(int l, int r) { return querylow(root, 1, n, l, r); } info querytop(int root, int l, int r, int ql, int qr) { if (l == ql && r == qr) return a[root].top; int mid = (l + r) / 2; info ans = (info) {0, 0}; if (mid >= ql) ans = ans + querytop(a[root].lc, l, mid, ql, min(qr, mid)); if (mid + 1 <= qr) ans = ans + querytop(a[root].rc, mid + 1, r, max(mid + 1, ql), qr); return ans; } info querytop(int l, int r) { return querytop(root, 1, n, l, r); } long long querymax(int root, int l, int r, int ql, int qr) { if (l == ql && r == qr) return a[root].Max; int mid = (l + r) / 2; long long ans = -LLONG_MAX; if (mid >= ql) ans = max(ans, querymax(a[root].lc, l, mid, ql, min(qr, mid))); if (mid + 1 <= qr) ans = max(ans, querymax(a[root].rc, mid + 1, r, max(mid + 1, ql), qr)); return ans; } long long querymax(int l, int r) { return querymax(root, 1, n, l, r); } long long querymin(int root, int l, int r, int ql, int qr) { if (l == ql && r == qr) return a[root].Min; int mid = (l + r) / 2; long long ans = LLONG_MAX; if (mid >= ql) ans = min(ans, querymin(a[root].lc, l, mid, ql, min(qr, mid))); if (mid + 1 <= qr) ans = min(ans, querymin(a[root].rc, mid + 1, r, max(mid + 1, ql), qr)); return ans; } long long querymin(int l, int r) { return querymin(root, 1, n, l, r); } } ST; int n, q; long long sum, a[MAXN]; void add(int l, int r, int d) { if (l != 1) sum -= abs(a[l]); sum -= abs(a[r + 1]); a[l] += d, ST.modify(l, a[l]); if (r != n) a[r + 1] -= d, ST.modify(r + 1, a[r + 1]); if (l != 1) sum += abs(a[l]); sum += abs(a[r + 1]); } void judge(long long &ans, int x, int l, int r) { if (l > r) return; long long tmp = sum + 2 * x; if (ST.queryval(l) <= 0) ans = max(ans, tmp - 2 * min(x * 1ll, abs(ST.querymax(l, r)))); else ans = max(ans, tmp - 2 * min(x * 1ll, abs(ST.querymin(l, r)))); } int main() { read(n); for (int i = 1; i <= n; i++) read(a[i]); for (int i = n; i >= 2; i--) { a[i] -= a[i - 1]; sum += abs(a[i]); } ST.init(n, a); read(q); while (q--) { int opt, l, r, x; read(opt), read(l), read(r), read(x); if (opt == 2) add(l, r, x); else { long long ans = 0; add(l, l, x); ans = max(ans, sum); add(l, l, -x); add(r, r, x); ans = max(ans, sum); add(r, r, -x); if (r - l <= 1) { writeln(ans); continue; } add(l + 1, l + 1, x); ans = max(ans, sum); add(l + 1, l + 1, -x); add(r - 1, r - 1, x); ans = max(ans, sum); add(r - 1, r - 1, -x); info tmp = ST.querylow(l + 1, r); info tnp = ST.querytop(l + 1, r); if (tnp.cnt != 0) { writeln(sum + 2 * x); continue; } if (tmp.cnt != 0) { add(tmp.pos, tmp.pos, x); ans = max(ans, sum); add(tmp.pos, tmp.pos, -x); if (tmp.pos != l) add(tmp.pos - 1, tmp.pos - 1, x), ans = max(ans, sum), add(tmp.pos - 1, tmp.pos - 1, -x); if (tmp.pos != r) add(tmp.pos + 1, tmp.pos + 1, x), ans = max(ans, sum), add(tmp.pos + 1, tmp.pos + 1, -x); judge(ans, x, l + 2, tmp.pos - 1); judge(ans, x, tmp.pos + 2, r - 1); } else judge(ans, x, l + 2, r - 1); writeln(ans); } } return 0; }
相关文章推荐
- 【codeforces】Codeforces Round #310 (Div. 1)【题解】
- Codeforces 658A B C || VK Cup 2016 - Round 1 (Div. 2 Edition) A B C题解
- 【CodeForces】CodeForces Round #466 (Div. 2) 题解
- 【CodeForces】CodeForces Round #467 (Div. 1 + Div. 2) 题解
- 【CodeForces】CodeForces Round #462 (Div. 1 + Div. 2) 题解
- 【codeforces】Codeforces Round #277 (Div. 2) 题解
- Codeforces Round #404 div2 (CodeForces - 785ABCD) 题解
- 【codeforces】Codeforces Round #283 (Div. 2) 【题解】
- 【codeforces】Codeforces Round #311 (Div. 2)only 【题解】
- 【codeforces】Codeforces Round #284 (Div. 1) 【题解】
- 【CodeForces】CodeForces Round #463 (Div. 1 + Div. 2) 题解
- codeforces 789 div2 题解(AB水题,C dp,D图论)
- codeforces div1 D题解
- 【CodeForces】CodeForces Round #464 (Div. 2) 题解
- codeforces 187 (div2)题解
- 【CodeForces】CodeForces Round #460 (Div. 2) 题解
- Codeforces Round #403 div2 (CodeForces - 782) 题解
- 【Codeforces Round #411 (Div. 1)】Codeforces 804D
- Codeforces Round #365 (Div. 2) 题解
- 小朋友学Codeforces(3):Round 453 DIV 2, B