2025省选模拟5
这场比较简单,T1 T2 赛后都是没调打完就过了。既然改完了,而且现在也不想写啥题那就还是补下题解吧。
T1 枇杷树
操作 \(m\le 300\),每次操作构成一颗新树。具体:用边权为 \(w\) 的边连接编号为 \(x\) 的树中的 \(u\) 号节点,编号为 \(y\) 的树中的 \(v\) 号节点。
询问每颗树中的点两两之间距离。
考虑把两颗树连起来的影响:
\(f_{k,u}\) 表示编号为 \(k\) 的树中所有点到 \(u\) 的距离和。
维护 \(f_{k,u}\):考虑 \(u\) 是在 \(x_k\) 中还是 \(y_k\) 中即可。具体:
\(dis_{k,i,j}\) 表示 \(T_k\) 中 \(i,j\) 两点间的距离。
维护 \(dis\):一样分类讨论即可。详细见代码。
点数很多记忆化就行了。
正确性:\(f,dis\) 的转移只有关键点有关,关键点数量级为 \(O(m)\),\(f\) 状态数 \(O(m^2)\),\(dis\) 状态数 \(O(m^3)\)。总复杂度 \(O(m^3)\)。
实现优美,是这种做法的最优解。赛后一遍过样例,没调直接交就过了。
#include <bits/stdc++.h>
#include <bits/extc++.h>using namespace std;
using namespace __gnu_pbds;#define IL inline
#define vec vector
#define bg begin
#define eb emplace_back
#define emp emplace
#define fi first
#define se second
#define mkp make_pair
using ubt = long long;
using uubt = unsigned long long;
using dub = double;
using puu = pair<ubt, ubt>;template <typename T = int>
IL T _R() {T s = 0, w = 1;char c = getchar();while (!isdigit(c)) w = c == '-' ? -1 : 1, c = getchar();while (isdigit(c)) s = s * 10 + c - 48, c = getchar();return s * w;
}constexpr int mod = 1e9 + 7;
constexpr int N = 300;
constexpr int maxN = N + 3;int m;ubt siz[maxN];
struct FUNCTION {IL ubt operator [] (int i) { return siz[i] % mod; }
} sz;int x[maxN], y[maxN];
ubt u[maxN], v[maxN];
int w[maxN];
ubt J[maxN];ubt ans[maxN];#define hshtb gp_hash_table
struct Has { IL uubt operator () (const puu &A) const { return A.fi * 13331ull + A.se; } };
hshtb<puu, ubt, Has> dis[maxN];
hshtb<ubt, ubt> f[maxN];ubt D(int k, ubt i, ubt j) {if (i == j) return 0;if (i > j) swap(i, j);if (dis[k][mkp(i, j)]) return dis[k][mkp(i, j)];if (j < J[k]) return D(x[k], i, j);if (i >= J[k]) return D(y[k], i - J[k], j - J[k]);ubt res = w[k] + D(x[k], i, u[k]) + D(y[k], j - J[k], v[k] - J[k]);return dis[k][mkp(i, j)] = res % mod;
}
ubt F(int k, ubt p) {if (!k && !p) return 0;if (f[k][p]) return f[k][p];ubt res = 0;if (p < J[k]) {res = sz[y[k]] * (w[k] + D(x[k], u[k], p)) % mod + F(y[k], v[k] - J[k]);res += F(x[k], p);} else {res = sz[x[k]] * (w[k] + D(y[k], v[k] - J[k], p - J[k])) % mod + F(x[k], u[k]);res += F(y[k], p - J[k]);}return f[k][p] = res % mod;
}int main() {freopen("loquat.in", "r", stdin);freopen("loquat.out", "w", stdout);m = _R();siz[0] = 1;for (int i = 1; i <= m; i++) {auto x = _R(), y = _R();auto u = _R<ubt>(), v = _R<ubt>() + siz[x];auto w = _R();siz[i] = siz[x] + siz[y];J[i] = siz[x];ans[i] = w * sz[x] % mod * sz[y] % mod;ans[i] += F(x, u) * sz[y] % mod + F(y, v - J[i]) * sz[x] % mod;ans[i] += ans[x] + ans[y];ans[i] %= mod;printf("%lld\n", ans[i]);::x[i] = x, ::y[i] = y, ::u[i] = u, ::v[i] = v, ::w[i] = w;}
}
上古遗迹
\(n,m\le 1e5,h_i\le 1e9\)
第 \(i\) 列有 \(h_i\) 个正方形。每次询问给出 \(l,r\) 问 \([l,r]\) 中的最大矩形面积。
\(l_i\) 表示最大的 \(j\) 使得 \(j<i \land h_j<h_i\)。等不等其实不怎么关心。 \(r_i\) 定义类似。
这部分可以单调栈,或者直接 dfs 配合 RMQ(ST 表)跑类似笛卡尔树的结构。
询问区间为 \([L,R]\)。分类讨论:
\(l_i\le L \land R \le r_i\)
- 显:\(A=\min\{h_{L\dots R}\}\times (R-L+1)\)
\(L\le l_i \land r_i \le R\)
- \(A=(r_i-l_i+1)\times h_i\),扫描线维护即可。
\(L\le l_i \land R < r_i\)
- \(A = (R-l_i+1)\times h_i=h_i R+(-l_i+1)h_i\)
\(l_i<L\land r_i\le R\)
- \(A = (r_i-L+1)\times h_i=-h_iL+(r_i+1)h_i\)
容易发现后两个是一次函数的形式,扫描线+李超线段树即可。
赛时分类讨论完了,好久没写李超线段树,没想到,还以为时单峰的,上了个线段树上三分。
精细实现,目前最优解。一遍码完,没调。
#include <bits/stdc++.h>using namespace std;#define IL inline
#define vec vector
#define bg begin
#define eb emplace_back
#define emp emplace
#define fi first
#define se second
#define mkp make_pair
using ubt = long long;
using uubt = unsigned long long;
using dub = double;
using pdd = pair<dub, dub>;IL int _R() {int x;scanf("%d", &x);return x;
}IL void ckx(auto &x, const auto &y) { (x < y) && (x = y); }constexpr dub inf = 1e18;constexpr int N = 2e5;
constexpr int maxN = N + 3;int n, h[maxN];struct ST {int st[20][maxN];IL int get(int i, int j) { return h[i] <= h[j] ? i : j; }IL void init() {for (int i = 1; i <= n; i++) st[0][i] = i;for (int t = 1; t <= 18; t++)for (int i = 1; i + (1 << t) - 1 <= n; i++)st[t][i] = get(st[t - 1][i], st[t - 1][i + (1 << (t - 1))]);}IL int ask(int l, int r) {int t = __lg(r - l + 1);return get(st[t][l], st[t][r - (1 << t) + 1]);}
} st;struct BIT {ubt t[maxN];IL void ins(int x, ubt v) {for (; x <= n; x += x & -x) ckx(t[x], v);}IL ubt ask(int x) {ubt res = 0;for (; x > 0; x -= x & -x) ckx(res, t[x]);return res;}
} bita;struct LC_Segtree {
#define ls (p << 1)
#define rs (p << 1 | 1)struct dat {int k;ubt b;dat() : b(-inf) {}dat(int k, ubt b) : k(k), b(b) {}} t[N * 4];IL ubt G(int x, const dat &A) { return (ubt)A.k * x + A.b; }void C(int L, int R, int k, ubt b, int p, int l, int r) {int mid = (l + r) >> 1;if (L <= l && r <= R) {if (G(mid, t[p]) < G(mid, {k, b})) swap(k, t[p].k), swap(b, t[p].b);if (G(l, t[p]) >= G(l, {k, b}) && G(r, t[p]) >= G(r, {k, b})) return;if (G(l, t[p]) < G(l, {k, b}))C(L, R, k, b, ls, l, mid);if (G(r, t[p]) < G(r, {k, b}))C(L, R, k, b, rs, mid + 1, r);return;}if (L <= mid)C(L, R, k, b, ls, l, mid);if (R > mid)C(L, R, k, b, rs, mid + 1, r);}void A(int K, int p, int l, int r, ubt &ans) {ckx(ans, G(K, t[p]));if (l == r) return;int mid = (l + r) >> 1;if (K <= mid) A(K, ls, l, mid, ans);else A(K, rs, mid + 1, r, ans);}IL void clear() { fill(&t[0], &t[N * 4], dat{0, (ubt)-inf}); }
#undef ls
#undef rs
} T;int tot;
struct node {int l, r, h;ubt s;
} a[maxN];void D(int l, int r) {int g = st.ask(l, r);a[++tot] = {l, r, h[g], (ubt)(r - l + 1) * h[g]};if (l < g) D(l, g - 1);if (g < r) D(g + 1, r);
}int m;
struct ques {int l, r, id;
} q[maxN];
ubt ans[maxN];int main() {freopen("relics.in", "r", stdin);freopen("relics.out", "w", stdout);n = _R(), m = _R();for (int i = 1; i <= n; i++) h[i] = _R();st.init();D(1, n);// assert(tot == n);for (int i = 1; i <= m; i++)q[i].l = _R(), q[i].r = _R(), q[i].id = i;for (int i = 1; i <= m; i++) {ubt H = h[st.ask(q[i].l, q[i].r)];ans[i] = H * (q[i].r - q[i].l + 1);}auto cmp1 = [&](const auto &A, const auto &B) {return A.l > B.l;};sort(a + 1, a + n + 1, cmp1);sort(q + 1, q + m + 1, cmp1);for (int i = 1, now = 1; i <= m; i++) {auto &[l, r, id] = q[i];while (now <= n && a[now].l >= l) {bita.ins(a[now].r, a[now].s);now++;}ckx(ans[id], bita.ask(r));}auto cmp2 = [&](const auto &A, const auto &B) {return A.l > B.l;};sort(a + 1, a + n + 1, cmp2);sort(q + 1, q + m + 1, cmp2);for (int i = 1, now = 1; i <= m; i++) {auto &[l, r, id] = q[i];while (now <= n && a[now].l >= l) {int k = a[now].h;ubt b = (ubt)(-a[now].l + 1) * a[now].h;T.C(a[now].l, a[now].r, k, b, 1, 1, n);now++;}T.A(r, 1, 1, n, ans[id]);}T.clear();auto cmp3 = [&](const auto &A, const auto &B) {return A.r < B.r;};sort(a + 1, a + n + 1, cmp3);sort(q + 1, q + m + 1, cmp3);for (int i = 1, now = 1; i <= m; i++) {auto &[l, r, id] = q[i];while (now <= n && a[now].r <= r) {int k = -a[now].h;ubt b = (ubt)(a[now].r + 1) * a[now].h;T.C(a[now].l, a[now].r, k, b, 1, 1, n);now++;}T.A(l, 1, 1, n, ans[id]);}for (int i = 1; i <= m; i++)printf("%lld\n", ans[i]);
}
T3 吞天得手
求字典序前 \(k\) 小的子序列的哈希值。
非常简单 DFS 即可,赛时想到正解,复杂度分析错了,拿了 70 分,不过是赛时最高分。
如果我们搜索了前 \(i\) 小的子序列了,那往它后面加一个数比改它之中的一个数字典序小。依次搜索它后面第 \(j\) 小的数即可。注意字典序相同的子序列应该同时搜,不然你就错了。
赛时想法是枚举后面加什么,然后用 vector
+ 二分找位置。“枚举后面加什么数”复杂度就是错的了。
正解是主席树找后缀第 \(k\) 小。
#include <bits/stdc++.h>using namespace std;#define IL inline
#define vec vector
#define bg begin
#define eb emplace_back
#define emp emplace
using ubt = long long;
using dub = double;template <typename T = int>
IL T _R() {T s = 0, w = 1;char c = getchar();while (!isdigit(c)) w = c == '-' ? -1 : 1, c = getchar();while (isdigit(c)) s = s * 10 + c - 48, c = getchar();return s * w;
}constexpr int mod = 998244353;
constexpr int N = 1e5;
constexpr int maxN = N + 3;int n, k, B;
int a[maxN];int pos[maxN], nt[maxN];int top[maxN], tot;
struct dat {int l, r, v;
} t[N * 30];
IL void upd(int p) { t[p].v = t[p].l ? t[t[p].l].v : t[t[p].r].v; }
void C(int K, int &p, int w, int l, int r) {p = ++tot;t[p] = t[w];if (l == r) return t[p].v = K, void();int mid = (l + r) >> 1;if (a[K] <= mid)C(K, t[p].l, t[w].l, l, mid);elseC(K, t[p].r, t[w].r, mid + 1, r);upd(p);
}
int Q(int L, int R, int p, int l, int r) {if (!p) return -1;if (L <= l && r <= R) return t[p].v;int mid = (l + r) >> 1, res = -1;if (L <= mid) res = Q(L, R, t[p].l, l, mid);if (R > mid && res == -1)res = Q(L, R, t[p].r, mid + 1, r);return res;
}void dfs(const multiset<int> &S, ubt H) {int lst = 0;multiset<int> T;while (true) {int tmp = Q(lst + 1, N, top[(*S.bg()) + 1], 1, N);if (tmp == -1) return;lst = a[tmp];ubt A = (H * B + lst) % mod;T.clear();for (auto &i : S) {tmp = Q(lst, lst, top[i + 1], 1, N);if (tmp == -1) break;while (tmp != -1) {T.emp(tmp);printf("%lld\n", A);if (!--k) exit(0);tmp = nt[tmp];}}dfs(T, A);}
}int main() {freopen("ttds.in", "r", stdin);freopen("ttds.out", "w", stdout);n = _R(), k = _R(), B = _R();for (int i = 1; i <= n; i++) a[i] = _R();memset(pos, -1, sizeof(pos));for (int i = n; i >= 1; i--) {nt[i] = exchange(pos[a[i]], i);C(i, top[i], top[i + 1], 1, N);}dfs({0}, 0);
}
感觉看完暴力也就理解这题了,所以把赛时暴力也放上了。
#include <bits/stdc++.h>using namespace std;#define IL inline
#define vec vector
#define eb emplace_back
#define bg begin
#define lb lower_bound
#define ub upper_bound
using ubt = long long;template <typename T = int>
IL T _R() {T s = 0, w = 1;char c = getchar();while (!isdigit(c)) w = c == '-' ? -1 : 1, c = getchar();while (isdigit(c)) s = s * 10 + c - 48, c = getchar();return s * w;
}constexpr int mod = 998244353;
constexpr int N = 1e5;
constexpr int maxN = N + 3;int n, k;
int B;int a[maxN];vec<int> Hs, pos[maxN];void dfs(const vec<int> &s, ubt H) {for (int &v : Hs) {vec<int> tmp;for (const int &i : s) {auto gps = ub(pos[v].bg(), pos[v].end(), i);for (auto it = gps; *it <= n; it++)tmp.eb(*it);}if (tmp.empty()) continue;ubt A = (H * B + v) % mod;for (int &i : tmp) {printf("%lld\n", A);if (!--k) exit(1);}dfs(tmp, A);}
}int main() {freopen("ttds.in", "r", stdin);freopen("ttds.out", "w", stdout);n = _R(), k = _R(), B = _R();for (int i = 2; i <= n; i++)pos[a[i] = _R()].eb(i), Hs.eb(a[i]);sort(Hs.bg(), Hs.end());Hs.resize(unique(Hs.bg(), Hs.end()) - Hs.bg());for (int &v : Hs) pos[v].eb(n + 2);dfs({1}, 0);
}
这场也太阳间了。有时间写题解是因为没过营。