# 【CodeForces】CodeForces Round #483 (Div. 1 + Div. 2) 题解

【比赛链接】

【题解链接】

【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() {
for (int i = 1; i <= n; 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() {
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() {
while (n--) {
long long a, b, 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() {
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]);
}
while (q--) {
int l, 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() {
long long now = 0;
for (int i = 1; i <= n; i++) {
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;
}

【思路要点】

• 对坐标离散化，使得坐标在$$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() {
top = 0;
for (int i = 1; i <= n; 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() {
for (int i = 2; i <= n; i++) {
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);
for (int i = 1; i <= n; i++)
up[i][0] = i;
for (int i = 1; i <= m; i++) {
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];
for (int i = 1; i <= q; i++) {
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;
}

• 广告
• 抄袭
• 版权
• 政治
• 色情
• 无意义
• 其他

120