2025CSP-S模拟赛16 比赛总结
2025CSP-S模拟赛16
T1 醉
简单题。也是场切了。
首先考虑答案是否存在。根据一个性质叫做一个点在树上离他最远的节点一定是直径的端点。记直径为端点分别为 \(U,V\)。然后分别算一下点 \(u\) 和 \(U,V\) 的距离是否 \(\geq d\) 即可。
考虑存在答案,那么不妨设 \(dis(u,U) \geq d\),则点 \(v\) 一定在 \(u\) 到 \(U\) 的这条路径上。然后就做完了。求出 \(u\) 和 \(U\) 的 lca 记为 \(lca\),判断 \(v\) 在 \(u,lca\) 的路径上还是在 \(U,lca\) 的路径上,然后跳 \(k\) 级祖先就行了。
#include <bits/stdc++.h>
using namespace std;
int read() {
int x = 0; char ch = getchar();
while (ch < '0' || ch > '9') ch = getchar();
while (ch >= '0' && ch <= '9') {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x;
}
const int N = 2e5 + 10;
int n, m;
vector<int> G[N];
int dep[N], fa[N][22];
int U, V, dd[N];
void dfs1(int x, int fr) {
dep[x] = dep[fr] + 1;
if (dep[x] > dep[U]) U = x;
fa[x][0] = fr;
for (int i = 1; i <= 20; i++) {
fa[x][i] = fa[fa[x][i - 1]][i - 1];
}
for (int y : G[x]) {
if (y == fr) continue;
dfs1(y, x);
}
}
void dfs2(int x, int fr) {
dd[x] = dd[fr] + 1;
if (dd[x] > dd[V]) V = x;
for (int y : G[x]) {
if (y == fr) continue;
dfs2(y, x);
}
}
int getlca(int x, int y) {
if (dep[x] < dep[y]) swap(x, y);
for (int i = 20; i >= 0; i--) {
if (dep[fa[x][i]] >= dep[y]) x = fa[x][i];
}
if (x == y) return x;
for (int i = 20; i >= 0; i--) {
if (fa[x][i] != fa[y][i]) {
x = fa[x][i];
y = fa[y][i];
}
}
return fa[x][0];
}
inline int getdis(int x, int y) {
return dep[x] + dep[y] - 2 * dep[getlca(x, y)];
}
int getfa(int x, int k) {
for (int i = 0; i <= 20; i++) {
if ((k >> i) & 1) {
x = fa[x][i];
}
}
return x;
}
int main() {
n = read();
for (int i = 1; i < n; i++) {
int x = read(), y = read();
G[x].push_back(y);
G[y].push_back(x);
}
dfs1(1, 0);
dfs2(U, 0);
m = read();
while (m--) {
int u = read(), d = read();
int d1 = getdis(u, U), d2 = getdis(u, V);
if (d1 < d && d2 < d) {
printf("-1\n");
continue;
}
if (d1 >= d) {
int lca = getlca(u, U);
if (d <= dep[u] - dep[lca]) {
printf("%d\n", getfa(u, d));
} else {
printf("%d\n", getfa(U, d1 - d));
}
} else {
int lca = getlca(u, V);
if (d <= dep[u] - dep[lca]) {
printf("%d\n", getfa(u, d));
} else {
printf("%d\n", getfa(V, d2 - d));
}
}
}
return 0;
}
T2 与
首先,考虑答案如何求解。不难想到可以用如下方法求解:
for (int i = 1; i <= qq; i++) {
int l = read(), r = read();
int sum = a[l];
for (int i = l + 1; i <= r; i++) {
if (a[i] & sum) sum |= a[i];
}
if (a[r] & sum) printf("Shi\n");
else printf("Fou\n");
}
然后发现这个满足结合律,考虑用线段树维护。
这个代码不难理解,直接看代码吧。。
#include <bits/stdc++.h>
using namespace std;
int read() {
int x = 0; char ch = getchar();
while (ch < '0' || ch > '9') ch = getchar();
while (ch >= '0' && ch <= '9') {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x;
}
const int N = 3e5 + 10;
int n, qq, a[N];
struct node {
int l, r, s[20];
} tree[4 * N];
#define lc p << 1
#define rc p << 1 | 1
void pushup(int p) {
for (int i = 0; i < 20; i++) {
tree[p].s[i] = tree[lc].s[i];
for (int j = 0; j < 20; j++) {
if ((1 << j) & tree[lc].s[i]) {
tree[p].s[i] |= tree[rc].s[j];
}
}
tree[p].s[i] |= tree[rc].s[i];
}
}
void build(int p, int l, int r) {
tree[p].l = l, tree[p].r = r;
if (l == r) {
for (int i = 0; i < 20; i++) {
if ((1 << i) & a[l]) tree[p].s[i] = a[l];
}
return;
}
int mid = l + r >> 1;
build(lc, l, mid), build(rc, mid + 1, r);
pushup(p);
}
int sum;
bool query(int p, int l, int r, int v) {
if (sum & v) return 1;
if (l <= tree[p].l && tree[p].r <= r) {
int x = 0;
for (int i = 0; i < 20; i++) {
if ((1 << i) & sum) x |= tree[p].s[i];
}
sum |= x;
return sum & v;
}
int mid = tree[p].l + tree[p].r >> 1;
int res = 0;
if (l <= mid) res |= query(lc, l, r, v);
if (res) return 1;
if (mid < r) res |= query(rc, l, r, v);
return res;
}
int main() {
n = read(), qq = read();
for (int i = 1; i <= n; i++) {
a[i] = read();
}
build(1, 1, n);
for (int i = 1; i <= qq; i++) {
int l = read(), r = read();
sum = a[l];
if (query(1, l + 1, r - 1, a[r])) {
printf("Shi\n");
} else printf("Fou\n");
}
return 0;
}

浙公网安备 33010602011771号