Educational Codeforces Round 7 部分题解
C. Not Equal on a Segment
脑子转不动,结果想了半天才做出来。。
有一个性质:\(a \geq b\) 且 \(a \leq b \Leftrightarrow a = b\)
所以我们需要维护一个区间最小值 \(\mathrm{minn}\) 和区间最大值 \(\mathrm{maxn}\),如果 \(\mathrm{minn} = \mathrm{maxn} = x\) 即是无解。
这题还让我们随便输出一个不等于 \(x\) 的数的位置,既然 \(\mathrm{maxn}, \mathrm{minn}\) 至少有一个不等于 \(x\),那我们只需要输出那个数的位置就好了。
做法也很简单,使用线段树维护四个信息:区间最大值、最小值和区间最大值、最小值所在的位置。挺好写的。
时间复杂度 \(O(n \log n)\)。
const int MAXN = 2e5 + 10;
int n, m;
int aa[MAXN];
namespace Segt {
struct Node {
int minn, maxn;
int minp, maxp;
Node() {minn = maxn = minp = maxp = 0;}
} segt[MAXN << 2];
#define ls (p << 1)
#define rs (p << 1 | 1)
Node Merge(Node pl, Node pr) {
Node ret;
if (pl.minn < pr.minn) { ret.minn = pl.minn; ret.minp = pl.minp; }
else { ret.minn = pr.minn; ret.minp = pr.minp; }
if (pl.maxn > pr.maxn) { ret.maxn = pl.maxn; ret.maxp = pl.maxp; }
else { ret.maxn = pr.maxn; ret.maxp = pr.maxp; }
return ret;
}
void Update(int p) {
segt[p] = Merge(segt[ls], segt[rs]);
}
void buildTree(int p, int l, int r, int *aa) {
if (l == r) {
segt[p].minn = segt[p].maxn = aa[l];
segt[p].minp = segt[p].maxp = l;
return;
} int mid = (l + r) >> 1;
buildTree(ls, l, mid, aa); buildTree(rs, mid + 1, r, aa);
Update(p);
}
Node Query(int p, int l, int r, int ll, int rr) {
if (l == ll && rr == r) return segt[p];
int mid = (l + r) >> 1;
if (rr <= mid) return Query(ls, l, mid, ll, rr);
else if (mid + 1 <= ll) return Query(rs, mid + 1, r, ll, rr);
else return Merge(Query(ls, l, mid, ll, mid), Query(rs, mid + 1, r, mid + 1, rr));
}
}
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n >> m;
rep (i, 1, n) cin >> aa[i];
Segt::buildTree(1, 1, n, aa);
rep (i, 1, m) {
int l, r, x; cin >> l >> r >> x;
Segt::Node res = Segt::Query(1, 1, n, l, r);
if (res.minn == x && res.maxn == x) puts("-1");
else {
if (res.minn == x) printf("%d\n", res.maxp);
else printf("%d\n", res.minp);
}
}
return 0;
}
实际上还有一种更简单的做法:求出每个位置往前走,第一个和它不同的数字在哪,然后先看右端点是不是和 x 相同,再看右端点往前走第一个和它不同的数字在不在左端点范围里。
const int MAXN = 2e5 + 10;
int n, m;
int aa[MAXN];
int prev[MAXN];
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
n = read(); m = read();
rep (i, 1, n) {
aa[i] = read();
if (aa[i] == aa[i - 1]) prev[i] = prev[i - 1];
else prev[i] = i - 1;
}
rep (i, 1, m) {
int l = read(); int r = read(); int x = read();
if (aa[r] != x) printf("%d\n", r);
else if (prev[r] >= l) printf("%d\n", prev[r]);
else puts("-1");
}
return 0;
}
D. Optimal Number Permutation
有趣的构造题。
考虑能不能把这个式子构造出 0,这种情况即是:\(\forall i \in [1, n - 1], d_i = n - i\),并且 \(d_n\) 可以是任意值。
例如 \(n = 6\) 时:
d1 = 5
d2 = 4
d3 = 3
d4 = 2
d5 = 1
接下来就胡乱手玩碰运气,尝试从小到大安排、从大到小安排,从前往后填、循环填……可以发现从小到大安排,循环找空填是一种似乎靠谱的构造方式:
1 3 5 5 3 1 2 4 6 4 2 6
从 \(1\) 到 \(n - 1\),依次找第一个能放得下当前数字的位置填进去。可以发现这样的数字结构是两段回文数字拼起来,而且最后一定会剩下两个空,放入 \(n\) 即可。
const int MAXN = 5e5 + 10;
int n;
int ans[MAXN << 1];
void Solve() {
int lenl = n, lenr = n - 1;
int stl = 1, str = stl + lenl;
int edl = stl + lenl - 1, edr = str + lenr - 1;
for (int i = 1; i <= n - 1; i += 2) {
int pos = (i + 1) >> 1;
int ul = stl + pos - 1;
int ur = edl - ul + 1;
ans[ul] = ans[ur] = i;
}
for (int i = 2; i <= n - 1; i += 2) {
int pos = i >> 1;
int ul = str + pos - 1;
int ur = edr - pos + 1;
ans[ul] = ans[ur] = i;
}
for (int i = 1; i <= (n << 1); ++i) if (!ans[i]) ans[i] = n;
}
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n;
Solve(); // both compatible with odd and even
rep (i, 1, (n << 1)) printf("%d ", ans[i]);
puts("");
return 0;
}
E. Ants in Leaves
很经典的题了。考虑贪心,对于所有根的儿子求出所有蚂蚁离开这个子树的最短时间,答案即是这些时间取 max。
怎么求每棵子树的最短时间?
考虑如果没有深度相同的点,那么答案就是最大深度,但是如果有了相同深度的点,这些相同深度的点就需要排队,所以不妨按顺序将它们的深度手动加一,深度加一就代表这个点需要多排一秒的队。这么一波处理完之后,答案即是最大深度。
const int MAXN = 5e5 + 10;
int n; std::vector<int> G[MAXN];
std::vector<int> depths;
void dfs(int u, int fa, int dep) {
if (G[u].size() == 1) depths.push_back(dep);
forall (G[u], i) {
int v = G[u][i];
if (v == fa) continue;
dfs(v, u, dep + 1);
}
}
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
n = read();
rep (i, 1, n - 1) {
int u = read(); int v = read();
G[u].push_back(v); G[v].push_back(u);
}
int ans = 0;
forall (G[1], i) {
int v = G[1][i];
depths.clear();
dfs(v, 1, 1);
std::sort(ALL(depths));
for (int i = 1; i < (int) depths.size(); ++i) {
depths[i] = std::max(depths[i], depths[i - 1] + 1);
}
ans = std::max(ans, depths[depths.size() - 1]);
} printf("%d\n", ans);
return 0;
}
F
数学题……