【比赛链接】
【题解链接】
【Div.2 A】Game
【思路要点】
- 排序,取中位数为答案。
- 时间复杂度\(O(NLogN)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 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]; int main() { int n; read(n); for (int i = 1; i <= n; i++) read(a[i]); sort(a + 1, a + n + 1); printf("%d\n", a[(n + 1) / 2]); return 0; }
【Div.2 B】Minesweeper
【思路要点】
- 模拟题意检验即可。
- 时间复杂度\(O(N*M)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 105; const int dx[8] = {0, 0, 1, 1, 1, -1, -1, -1}; const int dy[8] = {-1, 1, -1, 1, 0, -1, 1, 0}; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 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 n, m; char mp[MAXN][MAXN]; int main() { read(n), read(m); for (int i = 1; i <= n; i++) scanf("\n%s", mp[i] + 1); for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { if (mp[i][j] == '*') continue; int cnt = 0; if (mp[i][j] != '.') cnt = mp[i][j] - '0'; for (int k = 0; k < 8; k++) if (mp[i + dx[k]][j + dy[k]] == '*') cnt--; if (cnt != 0) { printf("NO\n"); return 0; } } printf("YES\n"); return 0; }
【Div.2 C/Div.1 A】Finite or not?
【思路要点】
- 令\(q'=\frac{q}{gcd(p,q)}\),问题等价于询问\(\frac{b^{\infty}}{q'}\)是不是整数。
- 不断地取\(g=gcd(q',b)\),并令\(q'=\frac{q'}{g}\),直到\(g=1\)为止。
- 检查是否有\(q'=1\)即可回答询问。
- 由于\(q'\)至多减少\(O(LogV)\)次,该做法的时间复杂度为\(O(NLog^2V)\),可能无法通过1s的时限。
- 注意到我们可以取\(g=gcd(q',g)\),得到的\(g\)是相同的。
- 而\(g\)至多减少\(O(LogV)\)次,每次减少会对时间复杂度产生\(O(1)\)的贡献,因此求解gcd的总时间复杂度降至\(O(NLogV)\),总体时间复杂度\(O(NLogV)\),可以通过本题。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 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(""); } long long gcd(long long x, long long y) { if (y == 0) return x; else return gcd(y, x % y); } int main() { int n; read(n); while (n--) { long long a, b, c; read(a), read(b), read(c); if (a % b == 0) printf("Finite\n"); else { long long g = gcd(a, b); b /= g; g = gcd(b, c); for (int i = 1; i <= 64; i++) { g = gcd(b, g); b /= g; } if (b == 1) printf("Finite\n"); else printf("Infinite\n"); } } return 0; }
【Div.2 D/Div.1 B】XOR-pyramid
【思路要点】
- 由题,令\(val_{i,j}\)为\(f(a_i,a_{i+1},...,a_j)\)的值。
- 则当\(i<j\),显然有\(val_{i,j}=val_{i,j-1}\ xor\ val_{i+1,j}\)。
- 预处理所有区间的\(val\)值,并DP求出区间内的最大值即可\(O(1)\)回答询问。
- 时间复杂度\(O(N^2+Q)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 5005; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 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], f[MAXN][MAXN], ans[MAXN][MAXN]; int main() { int n; read(n); for (int i = 1; i <= n; i++) read(a[i]), f[i][i] = a[i], ans[i][i] = a[i]; for (int len = 2; len <= n; len++) for (int i = 1, j = len; j <= n; i++, j++) { f[i][j] = f[i + 1][j] ^ f[i][j - 1]; ans[i][j] = f[i][j]; chkmax(ans[i][j], ans[i + 1][j]); chkmax(ans[i][j], ans[i][j - 1]); } int q; read(q); while (q--) { int l, r; read(l), read(r); writeln(ans[l][r]); } return 0; }
【Div.2 E/Div.1 C】Elevator
【思路要点】
- 考虑如何描述一个状态:我们需要记录当前已经接上过电梯的人数,电梯中存在的人所要去的楼层,以及电梯当前所在的楼层。
- 已经接上过电梯的人数是\(O(N)\)级别的,电梯当前所在的楼层共有9种。
- 由于电梯中的人数至多是4,如果我们认为空着的位置想要去的楼层为0,并保证这4个数有序,那么可能的状态共有\(\binom{13}{4}=715\)种。
- 通过适当地预处理,不难做到\(O(1)\)转移。
- 注意到所有转移的代价均为1,我们可以用BFS实现这个DP。
- 时间复杂度\(O(9*715*N)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; const int MAXLOG = 61; const int MAXP = 1e7 + 5; const long long INF = 9e18; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 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 SegmentTree { struct Node { int lc, rc; int sum; } a[MAXP]; int root, size; void insert(int &root, int depth, long long x, int d) { if (root == 0) root = ++size; a[root].sum += d; if (depth == -1) return; long long tmp = 1ll << depth; if (tmp & x) insert(a[root].rc, depth - 1, x, d); else insert(a[root].lc, depth - 1, x, d); } void insert(long long x, int d) { insert(root, MAXLOG, x, d); } long long query(int root, int depth, long long x, bool type) { if (depth == -1) return 0; long long tmp = 1ll << depth; if (type) { if (x & tmp) { if (a[a[root].rc].sum) return query(a[root].rc, depth - 1, x, type) + tmp; else return query(a[root].lc, depth - 1, x, type); } else { if (a[a[root].lc].sum) return query(a[root].lc, depth - 1, x, type); else return query(a[root].rc, depth - 1, x, type) + tmp; } } else { if (x & tmp) { if (a[a[root].lc].sum) return query(a[root].lc, depth - 1, x, type); else return -INF; } else { if (a[a[root].lc].sum) { long long tnp = query(a[root].lc, depth - 1, x, type); if (tnp > 0) return tnp; } if (a[a[root].rc].sum) return query(a[root].rc, depth - 1, x, true) + tmp; else return -INF; } } } long long query(long long x) { long long ans = query(root, MAXLOG, x, false); if (ans > 0) insert(ans, -1); return ans; } } ST; long long ans[MAXN]; int main() { int n; read(n); long long now = 0; for (int i = 1; i <= n; i++) { long long x; read(x); ST.insert(x, 1); } for (int i = 1; i <= n; i++) { long long tmp = ST.query(now); if (tmp <= 0) { printf("No\n"); return 0; } now ^= tmp; ans[i] = tmp; } printf("Yes\n"); for (int i = 1; i <= n; i++) write(ans[i]), putchar(' '); return 0; }
【Div.1 D】Arkady and Rectangles
【思路要点】
- 对坐标离散化,使得坐标在\(O(N)\)的范围内。
- 对X轴进行扫描线,并用线段树维护Y轴。
- 我们希望在线段树上维护信息\(Max\),在区间内可见且未被加入答案的,最大的颜色,若不存在,记-1。
- 如果我们能够维护这一信息,那么我们只需要不断地将该值加入答案,直到其为-1即可。
- 为此,我们需要额外维护以下信息:
- \(Colours\),定位到当前节点的颜色集合。
- \(Min\),区间内可见的最小颜色(包括0也考虑在内)。
- 更新时:
- 记子树中\(Max\)的最大值为\(Childmax\),子树中\(Min\)的最小值为\(Childmin\),\(Colours\)中最大值为\(Cmax\)。
- 若\(Colours\)不为空,且\(Cmax>Childmax\),
- 那么表示该区间被整个覆盖为了\(Cmax\)并且子树中的未被加入答案的颜色因为这一次覆盖全部不可见,因此当\(Cmax\)已经在答案中出现,或是\(Childmin>Cmax\)(也即该区间已经被标号更靠后的颜色完全覆盖),\(Max=-1\),否则\(Max=Cmax\)。
- 否则,也即\(Cmax≤Childmax\),\(Max=Childmax\)。
- 若\(Colours\)不为空且\(Cmax>Childmin\),\(Min=Cmax\)。
- 否则,\(Min=Childmin\)。
- 时间复杂度\(O(NLog^2N)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 200005; const int MAXP = 400005; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 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 Heap { priority_queue <int> Main, Delt; void push(int x) {Main.push(x); } void delt(int x) {Delt.push(x); } int query() { while (!Delt.empty() && Main.top() == Delt.top()) { Main.pop(); Delt.pop(); } if (Main.empty()) return -1; else return Main.top(); } }; struct SegmentTree { struct Node { Heap heap; int Max, Min, lc, rc; } a[MAXP]; int n, root, size; bool ans[MAXN]; void build(int &root, int l, int r) { root = ++size; a[root].Max = -1; a[root].Min = 0; if (l == r) return; int mid = (l + r) / 2; build(a[root].lc, l, mid); build(a[root].rc, mid + 1, r); } void init(int x) { n = x; root = size = 0; build(root, 1, n); } void update(int root) { int col = a[root].heap.query(); if (a[root].lc == 0) { if (col != -1 && !ans[col]) a[root].Max = col; else a[root].Max = -1; if (col != -1) a[root].Min = col; else a[root].Min = 0; } else { int childmax = max(a[a[root].lc].Max, a[a[root].rc].Max); int childmin = min(a[a[root].lc].Min, a[a[root].rc].Min); if (col != -1 && col > childmax) { if (ans[col] || col < childmin) a[root].Max = -1; else a[root].Max = col; } else a[root].Max = childmax; if (col != -1) a[root].Min = max(col, childmin); else a[root].Min = childmin; } } void insert(int root, int l, int r, int ql, int qr, int val) { if (l == ql && r == qr) { a[root].heap.push(val); update(root); return; } int mid = (l + r) / 2; if (mid >= ql) insert(a[root].lc, l, mid, ql, min(mid, qr), val); if (mid + 1 <= qr) insert(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, val); update(root); } void insert(int l, int r, int val) { insert(root, 1, n, l, r, val); } void delt(int root, int l, int r, int ql, int qr, int val) { if (l == ql && r == qr) { a[root].heap.delt(val); update(root); return; } int mid = (l + r) / 2; if (mid >= ql) delt(a[root].lc, l, mid, ql, min(mid, qr), val); if (mid + 1 <= qr) delt(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, val); update(root); } void delt(int l, int r, int val) { delt(root, 1, n, l, r, val); } void maintain(int root, int l, int r, int tmp) { if (a[root].Max != tmp) return; if (l == r) { update(root); return; } int mid = (l + r) / 2; maintain(a[root].lc, l, mid, tmp); maintain(a[root].rc, mid + 1, r, tmp); update(root); } void maintain() { while (a[root].Max != -1) { int tmp = a[root].Max; ans[tmp] = true; maintain(root, 1, n, tmp); } } int query(int n) { int sum = 1; for (int i = 1; i <= n; i++) sum += ans[i]; return sum; } } ST; vector <int> l[MAXN], r[MAXN], val[MAXN]; int n, xl[MAXN], xr[MAXN], yl[MAXN], yr[MAXN]; int xn, ym, top, tmp[MAXN]; int main() { read(n); top = 0; for (int i = 1; i <= n; i++) { read(xl[i]), read(yl[i]); read(xr[i]), read(yr[i]); tmp[++top] = xl[i]; tmp[++top] = xr[i]; } sort(tmp + 1, tmp + top + 1); xn = top = unique(tmp + 1, tmp + top + 1) - tmp - 1; for (int i = 1; i <= n; i++) { xl[i] = lower_bound(tmp + 1, tmp + top + 1, xl[i]) - tmp; xr[i] = lower_bound(tmp + 1, tmp + top + 1, xr[i]) - tmp; } top = 0; for (int i = 1; i <= n; i++) { tmp[++top] = yl[i]; tmp[++top] = yr[i]; } sort(tmp + 1, tmp + top + 1); ym = top = unique(tmp + 1, tmp + top + 1) - tmp - 1; for (int i = 1; i <= n; i++) { yl[i] = lower_bound(tmp + 1, tmp + top + 1, yl[i]) - tmp; yr[i] = lower_bound(tmp + 1, tmp + top + 1, yr[i]) - tmp; } ST.init(ym - 1); for (int i = 1; i <= n; i++) { l[xl[i]].push_back(yl[i]); r[xl[i]].push_back(yr[i] - 1); val[xl[i]].push_back(i); l[xr[i]].push_back(yl[i]); r[xr[i]].push_back(yr[i] - 1); val[xr[i]].push_back(-i); } for (int i = 1; i <= xn; i++) { for (unsigned j = 0; j < l[i].size(); j++) if (val[i][j] > 0) ST.insert(l[i][j], r[i][j], val[i][j]); else ST.delt(l[i][j], r[i][j], -val[i][j]); ST.maintain(); } writeln(ST.query(n)); return 0; }
【Div.1 E】NN country
【思路要点】
- 考虑一个询问\((x,y)\),令\(z=lca(x,y)\)。
- 若询问\((x,z)\)的答案为\(a\),询问\((y,z)\)的答案为\(b\),那么询问\((x,y)\)的答案或为\(a+b\),或为\(a+b-1\)。
- 先考虑如何快速求出\(a\)和\(b\)。
- 考虑询问\((x,z)\),我们贪心地向上走到\(x\)可以到达的最靠近根的点,重复这个过程直到当前点深度小于\(z\)的深度即可。
- 预处理出每个点向上可以走到的最靠近根的点\(low_i\),然后倍增预处理,即可在\(O(LogN)\)的时间内求出\(a\)和\(b\)。
- 现在我们考虑确定一个询问的答案是\(a+b\)还是\(a+b-1\)。
- 令\(x\)向上走\(a-1\)步能够走到的最靠近根的点为\(tx\),\(y\)向上走\(b-1\)步能够走到的最靠近根的点为\(ty\),当且仅当\(tx\)与\(ty\)可以相互直接到达,答案为\(a+b-1\)。
- \(tx\)与\(ty\)可以相互直接到达等价于存在一条线路使得其两端分别在\(tx\)和\(ty\)的子树内。
- 对树进行DFS,每当遇到一个线路的端点,将其另一端对应的点权值+1,若开始访问\(tx\)和结束访问\(tx\)时,\(ty\)的子树权值和不相同,则说明存在一条线路使得其两端分别在\(tx\)和\(ty\)的子树内,否则则不存在。
- 时间复杂度\(O(NLogN+MLogN+QLogN)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 200005; const int MAXLOG = 20; const int INF = 1e9; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 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 BinaryIndexTree { int n, a[MAXN]; void init(int x) { n = x; memset(a, 0, sizeof(a)); } void modify(int x, int d) { for (int i = x; i <= n; i += i & -i) a[i] += d; } int query(int l, int r) { int ans = 0; for (int i = r; i >= 1; i -= i & -i) ans += a[i]; for (int i = l - 1; i >= 1; i -= i & -i) ans -= a[i]; return ans; } } BIT; vector <int> a[MAXN], b[MAXN]; vector <int> pr[MAXN], home[MAXN], sum[MAXN]; int n, m, q, father[MAXN][MAXLOG], depth[MAXN]; int ans[MAXN], up[MAXN][MAXLOG]; int timer, dfn[MAXN], rit[MAXN]; void work(int pos) { for (unsigned i = 0; i < pr[pos].size(); i++) sum[pos].push_back(BIT.query(dfn[pr[pos][i]], rit[pr[pos][i]])); for (unsigned i = 0; i < b[pos].size(); i++) BIT.modify(dfn[b[pos][i]], 1); for (unsigned i = 0; i < a[pos].size(); i++) work(a[pos][i]); for (unsigned i = 0; i < pr[pos].size(); i++) if (sum[pos][i] != BIT.query(dfn[pr[pos][i]], rit[pr[pos][i]])) ans[home[pos][i]]--; } int steps(int pos, int dest) { if (pos == dest) return 0; int ans = 0; for (int i = MAXLOG - 1; i >= 0; i--) if (up[pos][i] > dest) { pos = up[pos][i]; ans += 1 << i; } if (up[pos][0] > dest) return INF; else return ans + 1; } int climb(int pos, int cnt) { for (int i = 0; i < MAXLOG; i++) if (cnt & (1 << i)) pos = up[pos][i]; return pos; } void getup(int pos) { for (unsigned i = 0; i < a[pos].size(); i++) { getup(a[pos][i]); chkmin(up[pos][0], up[a[pos][i]][0]); } } int lca(int x, int y) { if (depth[x] < depth[y]) swap(x, y); for (int i = MAXLOG - 1; i >= 0; i--) if (depth[father[x][i]] >= depth[y]) x = father[x][i]; if (x == y) return x; for (int i = MAXLOG - 1; i >= 0; i--) if (father[x][i] != father[y][i]) { x = father[x][i]; y = father[y][i]; } return father[x][0]; } void dfs(int pos) { for (int i = 1; i < MAXLOG; i++) father[pos][i] = father[father[pos][i - 1]][i - 1]; dfn[pos] = ++timer; for (unsigned i = 0; i < a[pos].size(); i++) { depth[a[pos][i]] = depth[pos] + 1; dfs(a[pos][i]); } rit[pos] = timer; } int main() { read(n); for (int i = 2; i <= n; i++) { read(father[i][0]); for (int j = 1; j < MAXLOG; j++) father[i][j] = father[father[i][j - 1]][j - 1]; a[father[i][0]].push_back(i); } depth[1] = 1; dfs(1); read(m); for (int i = 1; i <= n; i++) up[i][0] = i; for (int i = 1; i <= m; i++) { int x, y; read(x), read(y); b[x].push_back(y); b[y].push_back(x); int z = lca(x, y); chkmin(up[x][0], z); chkmin(up[y][0], z); } getup(1); for (int i = 1; i <= n; i++) for (int j = 1; j < MAXLOG; j++) up[i][j] = up[up[i][j - 1]][j - 1]; read(q); for (int i = 1; i <= q; i++) { int x, y; read(x), read(y); int z = lca(x, y); if (z == x) ans[i] = steps(y, z); else if (z == y) ans[i] = steps(x, z); else { int cx = steps(x, z); int cy = steps(y, z); ans[i] = cx + cy; int tx = climb(x, cx - 1); int ty = climb(y, cy - 1); pr[tx].push_back(ty); home[tx].push_back(i); } } BIT.init(n); work(1); for (int i = 1; i <= q; i++) if (ans[i] >= INF) writeln(-1); else writeln(ans[i]); return 0; }