当前位置: 首页 > news >正文

灵宝网站制作工作室/百度网站首页

灵宝网站制作工作室,百度网站首页,网站开发运营产品经理招聘,网站首页推广比赛链接 A. 出勤规划 题目大意 给定 n n n 个点 m m m 条边的 带权无向连通图 G G G,再给定 k k k 个点,记为 S k S_k Sk​,令 f ( u , v ) f(u, v) f(u,v) 表示 u u u 到 v v v 的最短距离。 这 k k k 个点构成了一张新图 G …

比赛链接

A. 出勤规划

题目大意

给定 n n n 个点 m m m 条边的 带权无向连通图 G G G,再给定 k k k 个点,记为 S k S_k Sk,令 f ( u , v ) f(u, v) f(u,v) 表示 u u u v v v 的最短距离。

k k k 个点构成了一张新图 G ′ G' G,每两个点 u , v u, v u,v 之间都有边,边权用原图 G G G 中的 f ( u , v ) f(u, v) f(u,v) 定义,求新图 G ′ G' G 最小生成树的边权和。

数据范围

  • 1 ≤ ∑ n , ∑ m ≤ 5 ⋅ 1 0 5 , 1 \leq \sum n, \sum m \leq 5 \cdot 10^5, 1n,m5105,
  • n − 1 ≤ m ≤ 5 ⋅ 1 0 5 , n - 1 \leq m \leq 5 \cdot 10^5, n1m5105,
  • 1 ≤ k ≤ n , 1 \leq k \leq n, 1kn,
  • 边权不超过 1 0 8 10^8 108

Solution

考虑 u u u v v v 的最短距离,要么是走 ( u , v , w ) (u, v, w) (u,v,w) 这条边( w w w 为边权),要么是通过某些结点中转。

因此可以考虑多源最短路,即以这 k k k 个给定点为源点,计算每个点到这些源点的最短路,同时记录 u u u 从哪个源点过来距离最短。

  • 这里的意思是这 k k k 个点一定存在一个 k i k_i ki u u u 的距离最短,而不是每个源点到 u u u 的最短距离都记录下来。

设这样得到了 d i s \rm{dis} dis f r o m \rm{from} from 数组,分别表示到这 k k k 个点的最短距离以及相应的源点。

那么我们可以断言,对于 ∀ k i , k j ∈ S k \forall k_i, k_j \in S_k ki,kjSk ( k i , k j ) (k_i, k_j) (ki,kj) 这条边在新图中的边权一定可以表示为 d i s [ u ] + w + d i s [ v ] dis[u] + w + dis[v] dis[u]+w+dis[v],其中 w w w 是原图中 u , v u, v u,v 这条边的边权。

这其实就相当于找一个中转边 ( u , v ) (u, v) (u,v) 计算两个源点 k i , k j k_i, k_j ki,kj之间的最短距离。

所以我们对这些新边排序做 k r u s k a l \rm{kruskal} kruskal 即可。

时间复杂度 O ( ( n + m ) log ⁡ m ) O(\rm{(n + m)\log m}) O((n+m)logm)

C++ Code

#include <bits/stdc++.h>using i64 = long long;template<class T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {for (auto &x: v) {is >> x;x--;}return is;
}struct DSU {std::vector<int> f, sz;DSU() {}DSU(int n) {init(n);}void init(int n) {f.resize(n);std::iota(f.begin(), f.end(), 0);sz.assign(n, 1);}int find(int x) {while (x != f[x]) {x = f[x] = f[f[x]];}return x;}int size(int x) {return sz[find(x)];}bool merge(int x, int y) {x = find(x);y = find(y);if (x == y) {return false;}sz[x] += sz[y];f[y] = x;return true;}bool same(int x, int y) {return find(x) == find(y);}
};constexpr i64 inf = std::numeric_limits<i64>::max() / 4;void solve() {int n, m, k;std::cin >> n >> m >> k;std::vector<std::array<int, 3>> eg(m);std::vector<std::vector<std::array<int, 2>>> adj(n);for (auto &[w, a, b]: eg) {std::cin >> a >> b >> w;a--, b--;if (a == b) {continue;}adj[a].push_back({w, b});adj[b].push_back({w, a});}std::vector<int> chosen(k);std::cin >> chosen;std::vector<i64> dis(n, inf);std::vector<int> from(n, -1);std::priority_queue<std::pair<i64, int>, std::vector<std::pair<i64, int>>, std::greater<>> q;for (int x: chosen) {q.emplace(dis[x] = 0, from[x] = x);}while (not q.empty()) {auto [d, x] = q.top(); q.pop();if (d != dis[x]) {continue;}for (const auto &[w, y]: adj[x]) {if (dis[y] > d + w) {from[y] = from[x];q.emplace(dis[y] = d + w, y);}}}std::vector<std::tuple<i64, int, int>> neg;for (const auto &[w, a, b]: eg) {int fa = from[a];int fb = from[b];if (fa != fb) {neg.emplace_back(dis[a] + w + dis[b], fa, fb);}}std::ranges::sort(neg);DSU dsu(n);int cnt = 0;i64 ans = 0;for (const auto &[w, a, b]: neg) {if (dsu.merge(a, b)) {ans += w;if (++cnt == k - 1) {break;}}}std::cout << ans << "\n";
}int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);int T = 1;std::cin >> T;while (T--) {solve();}return 0;
}

B. 二分炼狱

题面大意

大概就是说要找一个交替序列的和。

Solution

首先,这个题 p o s \rm{pos} pos 只有改变,可以看作对于同一个值 v a l \rm{val} val 的大小交替序列(保持下标递增)。

那我们不妨将 a a a 从小到大排序,然后用一棵线段树维护对于每个 a i a_i ai 的交替序列的长度以及序列和。

线段树维护的信息有:

  • l 0 / 1 l_{0/1} l0/1 表示交替序列的长度( 0 0 0 为大 1 1 1 为小);
  • s 0 / 1 s_{0/1} s0/1 表示交替序列的交替和( 0 0 0 为大 1 1 1 为小)。

在从左往右扫的过程中,设 k ∈ [ i , j ) k \in [i, j) k[i,j) 均有 a k = a i a_k = a_i ak=ai,我们先把有关 a i a_i ai 的信息全都在线段树中删掉,然后求落在 ( i , n ) (i, n) (i,n) 的交替序列和,最后把 a i a_i ai 的信息加回来,但是从 0 0 0 变成 1 1 1,即现在开始它会小于之后所有的元素。

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

C++ Code

#include <bits/stdc++.h>using i64 = long long;template<class Info>
struct SegmentTree {#define l(p) (p << 1)#define r(p) (p << 1 | 1)int n;std::vector<Info> info;SegmentTree() {}SegmentTree(int _n, Info _v = Info()) {init(_n, _v);}template<class T>SegmentTree(std::vector<T> _init) {init(_init);}void init(int _n, Info _v = Info()) {init(std::vector(_n, _v));}template<class T>void init(std::vector<T> _init) {n = _init.size();info.assign(4 << std::__lg(n), Info());auto build = [&](auto self, int p, int l, int r) {if (r - l == 1) {info[p] = _init[l];return;}int m = l + r >> 1;self(self, l(p), l, m);self(self, r(p), m, r);pull(p);};build(build, 1, 0, n);}void pull(int p) {info[p] = info[l(p)] + info[r(p)];}void modify(int p, int l, int r, int x, const Info &v) {if (r - l == 1) {info[p] = v;return;}int m = l + r >> 1;if (x < m) {modify(l(p), l, m, x, v);} else {modify(r(p), m, r, x, v);}pull(p);}void modify(int p, const Info &v) {modify(1, 0, n, p, v);}Info query(int p, int l, int r, int x, int y) {if (l >= y or r <= x) {return Info();}if (l >= x and r <= y) {return info[p];}int m = l + r >> 1;return query(l(p), l, m, x, y) + query(r(p), m, r, x, y);}Info query(int l, int r) {return query(1, 0, n, l, r);}template<class F>int findFirst(int p, int l, int r, int x, int y, F &&pred) {if (l >= y or r <= x) {return -1;}if (l >= x and r <= y and !pred(info[p])) {return -1;}if (r - l == 1) {return l;}int m = l + r >> 1;int res = findFirst(l(p), l, m, x, y, pred);if (res == -1) {res = findFirst(r(p), m, r, x, y, pred);}return res;}template<class F>int findFirst(int l, int r, F &&pred) {return findFirst(1, 0, n, l, r, pred);}template<class F>int findLast(int p, int l, int r, int x, int y, F &&pred) {if (l >= y or r <= x) {return -1;}if (l >= x and r <= y and !pred(info[p])) {return -1;}if (r - l == 1) {return l;}int m = l + r >> 1;int res = findLast(r(p), m, r, x, y, pred);if (res == -1) {res = findLast(l(p), l, m, x, y, pred);}return res;}template<class F>int findLast(int l, int r, F &&pred) {return findLast(1, 0, n, l, r, pred);}#undef l(p)#undef r(p)
};struct Info {std::array<int, 2> l{};std::array<i64, 2> s{};
};
Info operator+(const Info &a, const Info &b) {Info c;for (int i = 0; i < 2; i++) {c.l[i] = a.l[i] + b.l[i ^ (a.l[i] & 1)];c.s[i] = a.s[i] + b.s[i ^ (a.l[i] & 1)];}return c;
}template<class T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {for (auto &x: v) {is >> x;}return is;
}
template<class T>
std::ostream &operator<<(std::ostream &os, const std::vector<T> &v) {os << v[0];for (size_t i = 1; i < v.size(); i++) {os << " " << v[i];}return os;
}int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);int n;std::cin >> n;std::vector<int> a(n);std::cin >> a;SegmentTree<Info> sgt(n);for (int i = 0; i < n; i++) {sgt.modify(i, {1, 0, a[i], 0});}std::vector<int> ord(n);std::iota(ord.begin(), ord.end(), 0);std::ranges::sort(ord, [&](int i, int j) {return a[i] < a[j];});std::vector<i64> ans(n);for (int i = 0, j = 0; i < n; i = j) {while (j < n and a[ord[j]] == a[ord[i]]) {sgt.modify(ord[j++], {0, 0, 0, 0});}for (int k = i; k < j; k++) {ans[ord[k]] = sgt.query(ord[k] + 1, n).s[0];}for (int k = i; k < j; k++) {sgt.modify(ord[k], {0, 1, 0, a[ord[k]]});}}std::cout << ans << "\n";return 0;
}

C. BloomsTD6

哥们儿这个真不会,看 官解 也写不出来 😰.

D. 橘猫的背包问题

题目大意

一个 n ⋅ V n \cdot V nV 大概 1 0 7 10^7 107 01 01 01 背包,有 Q ∼ 1 0 4 Q \sim 10^4 Q104 个询问,每次询问只允许取 [ L , R ) [L, R) [L,R) 下标内的物品,求能得到的最大价值。

Solution

考虑线段树维护信息(左闭右开)。

  • l l l 表示线段树左端点;
  • r r r 表示线段树右端点;
  • m m m 表示区间中点;
  • d p l dpl dpl 是个二维数组,第一维长度 m − l + 1 m - l + 1 ml+1,第二维长度 V V V,它表示 [ m − i , m ) [m - i, m) [mi,m) 的逆序背包;
  • d p r dpr dpr 是个二维数组,第一维长度 r − m + 1 r - m + 1 rm+1,第二维长度 V V V,它表示 [ m , m + i ) [m, m + i) [m,m+i) 的顺序背包。

在求答案时,

  • 如果 r − l = 1 r - l = 1 rl=1,直接判断当前体积是否不超过询问的体积 P P P
  • 否则找到一个区间 [ l , r ) [l, r) [l,r) 使得 L ∈ [ l , m ) L \in [l, m) L[l,m) R ∈ [ m , r ) R \in [m, r) R[m,r),然后合并 d p l [ m − L ] dpl[m - L] dpl[mL] d p r [ R − m ] dpr[R - m] dpr[Rm],说人话就是取 s ∈ [ 0 , P ] s \in [0, P] s[0,P],分配给 d p l dpl dpl 的体积是 s s s,分配给 d p r dpr dpr 的体积是 P − s P - s Ps,最后遍历取最大价值。

时间复杂度 O ( n V log ⁡ n + Q max ⁡ ( n , V ) ) O(nV\log n + Q\max(n, V)) O(nVlogn+Qmax(n,V))

  • O ( n V log ⁡ n ) O(nV\log n) O(nVlogn) 是建树的时间,树高有 log ⁡ n \log n logn
  • O ( Q V ) O(QV) O(QV) 是指每次询问回答需要 O ( max ⁡ ( n , V ) ) O(\max(n, V)) O(max(n,V))
  • 这题的空间够大,数组大大方方开,一般 256 M B \rm{256MB} 256MB 能开 6 E 7 \rm{6E7} 6E7 i n t \rm{int} int 数组。

C++ Code

#include <bits/stdc++.h>constexpr int V = 10000;using Array = std::array<int, V + 1>;
using Vector = std::vector<Array>;const Vector E(1);struct Info {int l = 0;int m = 0;int r = 0;Vector dpl = E;Vector dpr = E;
};
std::vector<Info> info;std::vector<int> v;
std::vector<int> w;void build(int p, int l, int r) {if (r - l == 1) {return;}int m = l + r >> 1;info[p].l = l;info[p].r = r;info[p].m = m;build(p * 2, l, m);build(p * 2 + 1, m, r);auto &dpl = info[p].dpl;dpl.assign(m - l + 1, Array{});for (int i = m - 1, j = 0; i >= l; i--, j++) {dpl[j + 1] = dpl[j];for (int s = V; s >= v[i]; s--) {dpl[j + 1][s] = std::max(dpl[j + 1][s], dpl[j][s - v[i]] + w[i]);}}auto &dpr = info[p].dpr;dpr.assign(r - m + 1, Array{});for (int i = m, j = 0; i < r; i++, j++) {dpr[j + 1] = dpr[j];for (int s = V; s >= v[i]; s--) {dpr[j + 1][s] = std::max(dpr[j + 1][s], dpr[j][s - v[i]] + w[i]);}}
}
int merge(const Array &a, const Array &b, int P) {int ans = 0;for (int s = 0; s <= P; s++) {ans = std::max(ans, a[s] + b[P - s]);}return ans;
}
int query(int p, int l, int r, int P) {if (r - l == 1) {return v[l] <= P ? w[l]: 0;}int m = info[p].m;if (info[p].l <= l and l < m and m < r and r <= info[p].r) {return merge(info[p].dpl[m - l], info[p].dpr[r - m], P);}if (r <= m) {return query(p * 2, l, r, P);}return query(p * 2 + 1, l, r, P);
}template<class T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {for (auto &x: v) {is >> x;}return is;
}int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);int n;std::cin >> n;v.resize(n);w.resize(n);std::cin >> v >> w;info.assign(4 << std::__lg(n), Info()); build(1, 0, n);int Q;std::cin >> Q;for (int _ = 0, lst = 0; _ < Q; _++) {int iL, iR, iP;std::cin >> iL >> iR >> iP;int L = (iL + lst) % n + 1;int R = (iR + lst) % n + 1;if (L > R) {std::swap(L, R);}int P = (iP + lst) % V + 1;L--;std::cout << (lst = query(1, L, R, P)) << "\n";}    return 0;
}

E. GDUT = 1

题目大意

给定一个由 G , D , U , T , + \rm{G, D, U, T, +} G,D,U,T,+ 组成的表达式(保证合法),问是否存在对这四个字母 0 0 0 9 9 9 的赋值(可以有前导零),使得这个表达式的值 = x = x =x

Solution

容易想到,我们对这个字母统计它们在每个幂次上的贡献,比如 G G G 1 0 k 10^k 10k 位上出现了 c c c 次,那么它对 1 0 k 10^k 10k 次幂贡献为 c c c

由于 0 ≤ x ≤ 1 0 18 0 \leq x \leq 10^{18} 0x1018,所以凡是出现 k > 18 k > 18 k>18 的,那个字母的赋值只能为 0 0 0

然后我们计算每个字母每个幂次的贡献上限(即总的贡献不能超过 x x x),如果有不符合的也只能置为 0 0 0

这样我们就能愉快枚举 G , D , U , T G, D, U, T G,D,U,T 了。

T T T 最大是 1 0 5 10^5 105,如果直接 1 0 4 10^4 104 枚举大概率超时,所以我们可以用折半搜索的思想,先枚举前一半( G , D G, D G,D),然后用哈希表存下来每个值对应的 G , D G, D G,D 对,接着再枚举另一半 U , T U, T U,T,这样能把单次复杂度降到 1 0 2 10^2 102

时间复杂度 O ( ∑ n + T ⋅ 1 0 2 ) O(\sum n + T \cdot 10^2) O(n+T102)

  • 上限 1 0 7 10^7 107,但是常数稍大,所以时限还是有点紧的。

C++ Code

#include <bits/stdc++.h>using i64 = long long;constexpr i64 inf = 1E18;std::unordered_map<char, int> ump {{'G', 0},{'D', 1},{'U', 2},{'T', 3}
};
std::vector<i64> p10;
void init(int n) {p10.assign(n + 1, 1);for (int i = 1; i <= n; i++) {p10[i] = p10[i - 1] * 10;}
}
template<class T, size_t L>
std::ostream &operator<<(std::ostream &os, const std::array<T, L> &a) {os << a[0];for (size_t i = 1; i < L; i++) {os << " " << a[i];}return os;
}void solve() {std::string s;std::cin >> s;i64 x;std::cin >> x;int n = s.size();std::array<i64, 4> pows{};std::array<std::array<int, 18 + 1>, 4> cnts{};for (int i = 0, j = 0; i < n; i = j + 1) {j = i + 1;while (j < n and s[j] != '+') {j++;}for (int k = j - 1, p = 0; k >= i; k--, p++) {int id = ump[s[k]];if (p > 18) {pows[id] = -1;} else {cnts[id][p]++;}}}for (int i = 0; i < 4; i++) {if (pows[i] != -1) {for (int j = 0; j <= 18; j++) {if (cnts[i][j] == 0) {continue;}if (inf / cnts[i][j] < p10[j]) {pows[i] = -1;break;}if (pows[i] > inf - cnts[i][j] * p10[j]) {pows[i] = -1;break;}pows[i] += cnts[i][j] * p10[j];}}}auto print = [&](const std::array<int, 4> &a) {std::cout << "YES\n";std::cout << a << "\n";};std::unordered_map<i64, std::pair<int, int>> pair{};for (int g = 0; g < (pows[0] > 0 ? 10: 1); g++) {for (int d = 0; d < (pows[1] > 0 ? 10: 1); d++) {i64 res = g * pows[0] + d * pows[1];if (res == x) {print({g, d, 0, 0});return;}if (res < x) {pair[res] = std::pair {g, d};}}}for (int u = 0; u < (pows[2] > 0 ? 10: 1); u++) {for (int t = 0; t < (pows[3] > 0 ? 10: 1); t++) {i64 res = u * pows[2] + t * pows[3];if (res == x) {print({0, 0, u, t});return;}if (res < x and pair.contains(x - res)) {auto [g, d] = pair[x - res];print({g, d, u, t});return;}}}std::cout << "NO\n";
}int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);init(18);int T = 1;std::cin >> T;while (T--) {solve();}return 0;
}

F. 排队

题目大意

将一个序列分成左右两份(可以空),每一份都交给一个理发师(具体排队流程可以去题目里看),会得到两个处理时间,求两个时间总和的最小值。

Solution

首先简要的思路就是前后缀分解 + 枚举,但是这里的前后缀不好处理。

我们以前缀为例,后缀直接翻转处理即可。

考虑把时间划分为若干连续段,每个段内一定存在至少一个人,且有些段存在一些排队的人,每个段所占的区间为 [ t 0 , t 0 + c ⋅ k ] [t_0, \ t_0 + c \cdot k] [t0, t0+ck]

  • t 0 t_0 t0 表示之前某个人的达到时间;
  • c c c 表示段内总人数,段与段之间没有交集。

这样划分之后,假设我们从前往后枚举,现在加入了一个到达时间为 t t t 的顾客:

  • 如果他来得最早/最晚,就先给他在最前面/最后面单开一个段;
  • 否则我们可以找到它在段 [ t 0 , t 0 + c ⋅ k ] [t_0, \ t_0 + c \cdot k] [t0, t0+ck] 内,它一定在其中一个小区间内,不妨设为 [ t 0 + ( c 0 − 1 ) ⋅ k , t 0 + c 0 ⋅ k ] , 0 < c 0 ≤ c . [t_0 + (c_0 - 1) \cdot k, \ t_0 + c_0 \cdot k], \ \ 0 < c_0 \leq c. [t0+(c01)k, t0+c0k],  0<c0c. 设在这个小区间中,仍在等待且比他早到或同时到的有 c n t l cnt_l cntl 个,那么这些人不受影响,剩下的 ( c − c 0 ) − c n t l (c - c_0) - cnt_l (cc0)cntl 个人的等待时间全部增加 k k k,而他自己的等待时间为 ( t 0 + c 0 ⋅ k ) − t + c n t l ⋅ k , (t_0 + c_0 \cdot k) - t + cnt_l \cdot k, (t0+c0k)t+cntlk, 这样整体的等待时间就增加了 ( t 0 + c 0 ⋅ k ) − t + c n t l ⋅ k + ( ( c − c 0 ) − c n t l ) ⋅ k , (t_0 + c_0 \cdot k) - t + cnt_l \cdot k + ((c - c_0) - cnt_l) \cdot k, (t0+c0k)t+cntlk+((cc0)cntl)k, 化简一下就是 ( t 0 + c ⋅ k ) − t . (t_0 + c \cdot k) - t. (t0+ck)t. 我们发现,这其实相当于非抢占式调度,也就是这个顾客是 t t t 时间来的,我一直拖到前 c c c 个顾客都理完发才服务他,这样得到的等待时间增量也是 ( t 0 + c ⋅ k ) − t (t_0 + c \cdot k) - t (t0+ck)t,不过是全堆在这位 t t t 到达的顾客身上了。

上面处理了新到顾客的初始插入情况,还要处理接下来可能的局面:

  • 即这段 [ t 0 , t 0 + c ⋅ k ] [t_0, \ t_0 + c \cdot k] [t0, t0+ck] 变成 [ t 0 , t 0 + ( c + 1 ) ⋅ k ] [t_0, \ t_0 + (c + 1) \cdot k] [t0, t0+(c+1)k] 后,有可能和后面一段甚至好几段 [ t 1 , t 1 + c ′ ⋅ k ] [t_1, \ t_1 + c' \cdot k] [t1, t1+ck] 撞上了,这些段是需要合并的。

合并的原则和上述插入一模一样,不过是 t t t 发生了变化,从原来的给定 t t t,变成了 t ′ = t 0 + ( c + 1 ) ⋅ k t'= t_0 + (c + 1) \cdot k t=t0+(c+1)k,而相对应的,上文的 t 0 + c ⋅ k t_0 + c \cdot k t0+ck 就变为 t 1 t_1 t1;我们只要一直合并到右侧没有段或者和下一段没有交集为止。

不过等待时间增量的计算稍有不同:

  • 由于插入之前一定有 t 0 + c ⋅ k < t 1 t_0 + c \cdot k < t_1 t0+ck<t1,所以现在也一定有 t 1 ≤ t 0 + ( c + 1 ) ⋅ k < t + k t_1 \leq t_0 + (c + 1) \cdot k < t + k t1t0+(c+1)k<t+k
  • 这里要注意的是, t t t 时间插入的顾客 不能 再作为非抢占式的对象;换句话说,正是因为 t t t 时刻插入了这个顾客,才导致两个段合并,所以现在后一个段的 c ′ c' c 个顾客应该让步,也就是每个人的等待时间都延后 ( t 0 + ( c + 1 ) ⋅ k ) − t 1 , (t_0 + (c + 1) \cdot k) - t_1, (t0+(c+1)k)t1, 总的等待时间增加 c ′ ( ( t 0 + ( c + 1 ) ⋅ k ) − t 1 ) . c' ((t_0 + (c + 1) \cdot k) - t_1). c((t0+(c+1)k)t1).

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

C++ Code

#include <bits/stdc++.h>using i64 = long long;template<class T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {for (auto &x: v) {is >> x;}return is;
}void solve() {int n, k;std::cin >> n >> k;std::vector<int> time(n);std::cin >> time;    auto work = [&]() {std::vector<i64> res{0};res.reserve(n + 1);std::map<int, int> mp;i64 cost = 0;for (int t: time) {auto it = mp.upper_bound(t);if (it == mp.begin()) {mp[t] = 1;it = prev(it);} else {it = prev(it);i64 nt = it->first + static_cast<i64>(it->second) * k;if (t <= nt) {cost += nt - t;it->second++;} else {mp[t] = 1;it = next(it);}}while (next(it) != mp.end()) {auto jt = next(it);i64 nt = it->first + static_cast<i64>(it->second) * k;auto [t, c] = *jt;if (t > nt) {break;}cost += (nt - t) * c;it->second += c;mp.erase(jt);}res.push_back(cost);}return res;};auto pre = work();std::ranges::reverse(time);auto suf = work();std::ranges::reverse(suf);i64 ans = std::numeric_limits<i64>::max() / 4;for (int i = 0; i <= n; i++) {ans = std::min(ans, pre[i] + suf[i]);}std::cout << ans << "\n";
}int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);int T = 1;std::cin >> T;while (T--) {solve();}return 0;
}

G. Jump Sort

题目大意

给定一个长度为 n n n 的排列 a a a。现在选定一个 x ∈ [ 1 , n ] x \in [1, n] x[1,n] 后,可以交换任意满足 ∣ i − j ∣ = x |i - j| = x ij=x a i , a j a_i, a_j ai,aj,问 x x x 有多少种选法使得 a a a 变为升序。

Solution

交换的下标组是 i , i + x , i + 2 x , ⋯ i, i + x, i + 2x, \cdots i,i+x,i+2x,,组间无法交换,组内随便换,因此 [ i ] [i] [i] 这一组就要求最终换完后的 a a a 值是 i , i + x , i + 2 x , ⋯ i, i + x, i + 2x, \cdots i,i+x,i+2x,,也就是说 a i + k x = i + m x . a_{i + kx} = i + mx. ai+kx=i+mx. 移项后可以得到 a i + k x − ( i + k x ) = ( m − k ) x . a_{i + kx} - (i + kx) = (m - k)x. ai+kx(i+kx)=(mk)x. 如果 m = k m = k m=k,说明一开始就有 a i = i a_i = i ai=i,那就不需要换,否则我们发现, a i − i a_i - i aii x x x 的倍数。

所以我们只要找 g = gcd ⁡ i = 1 n ( a i − i ) g = \gcd \limits_{i = 1}^{n}(a_i - i) g=i=1gcdn(aii) 的所有因数即可。特别的,若 g = 0 g = 0 g=0,答案为 n n n,也就是怎么选都有序。

时间复杂度 O ( n + n ) = O ( n ) O(n + \sqrt{n}) = O(n) O(n+n )=O(n)

C++ Code

#include <bits/stdc++.h>template<class T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {for (auto &x: v) {is >> x;x--;}return is;
}int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);int n;std::cin >> n;std::vector<int> a(n);std::cin >> a;int g = 0;for (int i = 0; i < n; i++) {g = std::gcd(g, std::abs(a[i] - i));}if (g == 0) {std::cout << n << "\n";return 0;}auto calc = [&](int x) {int res = 0;for (int i = 1; i * i <= x; i++) {if (x % i == 0) {res++;if (i * i != x) {res++;}}}return res;};std::cout << calc(g) << "\n";return 0;
}

H. 可爱串串

Solution

直接前后缀分解,然后遍历求最大值。

时间复杂度 O ( n ) O(n) O(n)

C++ Code

#include <bits/stdc++.h>using i64 = long long;void solve() {int n;std::cin >> n;std::string s;std::cin >> s;std::vector<int> pre(n + 1);for (int i = 0; i < n; i++) {pre[i + 1] = pre[i] + (s[i] - '0');}i64 ans = -1E18;for (int i = 1; i < n; i++) {ans = std::max(ans, static_cast<i64>(pre[i] - (i - pre[i])) * ((pre[n] - pre[i]) - ((n - i) - (pre[n] - pre[i]))));}std::cout << ans << "\n";
}int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);int T = 1;std::cin >> T;while (T--) {solve();}return 0;
}

I. 好想成为人类啊

题目大意

S S S 中包含若干个 T T T,也就是存在若干区间 [ l , r ] [l, r] [l,r] 使得 S [ l . . . r ] = T S[l...r] = T S[l...r]=T,求最少要选择多少个点使得每个区间都存在一个被选点。

Solution

很经典的贪心,把区间按照右端点 r r r 排序,如果当前选点仍能在区间内,就略过,否则选择新区间的右端点。

而判断哪些区间是 T T T 可以用 k m p \rm{kmp} kmp。当然这题数据有点水,所以暴力匹配也可以。

时间复杂度 O ( ∑ ( ∣ S ∣ + ∣ T ∣ ) ) O(\sum (|S| + |T|)) O((S+T))

  • 可能是由于单测 n , m ≤ 1000 n, m \leq 1000 n,m1000,暴力 n m nm nm 的复杂度远低于 ( 2 14 ) 2 (2^{14})^2 (214)2

C++ Code

#include <bits/stdc++.h>void solve() {int n, m;std::cin >> n >> m;std::string S, T;std::cin >> S >> T;std::vector<int> nxt(m, -1);for (int i = 1, j = -1; i < m; i++) {while (j != -1 and T[i] != T[j + 1]) {j = nxt[j];}if (T[i] == T[j + 1]) {j++;}nxt[i] = j;}std::vector<std::pair<int, int>> seg;for (int i = 0, j = -1; i < n; i++) {while (j != -1 and S[i] != T[j + 1]) {j = nxt[j];}if (S[i] == T[j + 1]) {j++;}if (j == m - 1) {seg.emplace_back(i, i - m);}}if (seg.empty()) {std::cout << 0 << "\n";return;}std::ranges::sort(seg);int lst = -1;int cnt = 0;for (const auto &[r, l]: seg) {if (lst <= l) {lst = r;cnt++;}}std::cout << cnt << "\n";
}int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);int T = 1;std::cin >> T;while (T--) {solve();}return 0;
}

J. 11 背包

哈哈不会 😋

K. 橘猫的后缀问题

题目大意

给定一个长度为 n n n 的字符串 S S S,现在给定 Q Q Q 个操作,每个操作要么改变某个 S x S_x Sx,要么询问 S [ x . . . n ] S[x...n] S[x...n] 这个后缀在所有后缀中的排名。

Solution

由于数据的随机性,这题可以乱搞 🤣

也就是我们只检查所有后缀的前三位(不满的话补后缀零),用一个 27 27 27 进制数表示,记为 v a l val val。如果已经有 v a l val val 小于 S [ x . . . n ] S[x...n] S[x...n] v a l val val,那么排名 + 1 +1 +1,否则找相同的 v a l val val,除了自己都从第四位开始往后比较一遍字典序。

由于要快速算小于某个值的数的个数,因此要用到树状数组或线段树。

是的就是这样,因为数据是随机的,所以很快。

时间复杂度 O ( 可以过 ) O(可以过) O(可以过)

C++ Code

#include <bits/stdc++.h>template<class T>
struct Fenwick {int n, down;std::vector<T> a;Fenwick(int _n = 0): down(0) {init(_n);}Fenwick(std::vector<int> _a, int op): down(0) {init(_a, op);}void init(int _n) {n = _n;a.assign(n, T{});}void init(std::vector<int> _a, int op) {assert(op == 0 or op == 1);if (op == 0) {init(_a.size());for (int i = 0; i < _a.size(); i += 1) {add(i, _a[i]);}} else {int max = *std::max_element(_a.begin(), _a.end());int min = *std::min_element(_a.begin(), _a.end());init(max - min + 1);for (int x: _a) {add(x - min, 1);}down = min;}}int lowbit(int x) {return x & -x;}void add(int x, const T &v) {for (int i = x + 1; i <= n; i += lowbit(i)) {a[i - 1] = a[i - 1] + v;}}void clear() {a.assign(n, T{});}T sum(int x) {T ans{};for (int i = x; i > 0; i -= lowbit(i)) {ans = ans + a[i - 1];}return ans;}T sum(int l, int r) {return sum(r) - sum(l);}int kth(const T &k) {int x = 0;T cur{};for (int i = 1 << std::__lg(n); i; i /= 2) {if (x + i <= n and cur + a[x + i - 1] <= k) {x += i;cur = cur + a[x - 1];}}return x;}
};constexpr int L = 3;
constexpr int B = 27;
constexpr int N = (B - 1) * (B * (B + 1) + 1);int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);int n;std::cin >> n;std::string S;std::cin >> S;S += char('a' - 1);S += char('a' - 1);auto calc = [&](int i) {int val = 0;for (int j = i; j < i + L; j++) {val = val * B + (S[j] - 'a' + 1);}return val;};Fenwick<int> fen(N);std::map<int, std::set<int>> mp;std::vector<int> suf(n);for (int i = 0; i < n; i++) {suf[i] = calc(i);mp[suf[i]].insert(i);fen.add(suf[i], 1);}int Q;std::cin >> Q;while (Q--) {int o, x;std::cin >> o >> x;if (o == 1) {char c;std::cin >> c;for (int i = std::max(0, x - L); i < x; i++) {mp[suf[i]].erase(i);fen.add(suf[i], -1);}S[x - 1] = c;for (int i = std::max(0, x - L); i < x; i++) {suf[i] = calc(i);mp[suf[i]].insert(i);fen.add(suf[i], 1);}} else {x--;int ans = fen.sum(suf[x]) + 1;for (int i: mp[suf[x]]) {if (i != x) {for (int l = L; ; l++) {if (S[i + l] != S[x + l]) {ans += S[i + l] < S[x + l];break;}}}}std::cout << ans << "\n";}}return 0;
}

L. 字符串匹配太多了!

题目大意

定义了一个后缀匹配数组,要求构造字符串 T \rm{T} T,使得 m a t c h ( S 1 , T ) = m a t c h ( S 2 , T ) \rm{match(S_1, T) = match(S_2, T)} match(S1,T)=match(S2,T)

Solution

如果 S 1 = S 2 S_1 = S_2 S1=S2,输出 S 1 S_1 S1 即可。

否则我们考虑极端情况,即 ∣ T ∣ = 1 |T| = 1 T=1

我们只要能找到一个 S 1 S_1 S1 S 2 S_2 S2 都不包含 的字母 ∈ { a , b , c } \in \{a, b, c\} {a,b,c},那么 m a t c h \rm{match} match 一定都是 0 0 0,输出即可,否则一定无法构造出 T T T 满足条件。

这个证明不太会,大家可以感性理解一下(

时间复杂度 O ( ∑ n ) O(\sum n) O(n)

C++ Code

#include <bits/stdc++.h>using namespace std::literals::string_literals;void solve() {int n;std::cin >> n;std::string S1, S2;std::cin >> S1 >> S2;if (S1 == S2) {std::cout << "YES\n";std::cout << n << "\n";std::cout << S1 << "\n";return;}for (auto c: "abc"s) {auto check = [&](char c) {for (int i = 0; i < n; i++) {if ((S1[i] == c) != (S2[i] == c)) {return false;}}return true;};if (check(c)) {std::cout << "YES\n";std::cout << 1 << "\n";std::cout << c << "\n";return;}}std::cout << "NO\n";
}int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);int T = 1;std::cin >> T;while (T--) {solve();}return 0;
}

M. 橘猫的后缀问题

题目大意

n n n 天要出去 k k k 天,共 m m m 家餐厅,要求不能连续两天去同一个餐厅,求总共有多少种出门方式,答案对 1 0 9 + 7 10^9 + 7 109+7 取模。

Solution

考虑连续出一段门,长度为 L L L,那么第一天随便选,后面 L − 1 L - 1 L1 天只要保证和前一天不同即可,所以有 m ( m − 1 ) L − 1 m(m - 1)^{L - 1} m(m1)L1 种情况。

下面考虑将 k k k 天切割为 s s s 段,每一段之间都至少有一天不出门,那么切割方式有 ( k − 1 s − 1 ) {k - 1 \choose s - 1} (s1k1) 种,因为每一段不能是空的。

根据第一段的结论,此时这 s s s 段内部选择餐厅有 m s ( m − 1 ) k − s m^s(m - 1)^{k - s} ms(m1)ks 种情况。

现在继续考虑把这 s s s 段放入 n n n 天里,其实就相当于把剩下的 n − k n - k nk 个不出门的天数匀到这 s s s 段的两边。设 x i x_i xi 表示分给第 i i i i + 1 i + 1 i+1 段之间的不出门天数,若 i ∈ [ 1 , s ) i \in [1, s) i[1,s) 则要求 x i > 0 x_i > 0 xi>0,否则 i = 0 i = 0 i=0 i = s i = s i=s,只要 x i ≥ 0 x_i \geq 0 xi0 即可。现在要求的就是 x 0 + x 1 + x 2 + ⋯ + x s − 1 + x s = n − k , x_0 + x_1 + x_2 + \cdots + x_{s - 1} + x_{s} = n - k, x0+x1+x2++xs1+xs=nk, 这个方程解的个数,我们令 { x i ′ = x i , i ∈ [ 1 , s ) , x i ′ = x i + 1 , i = 0 ∣ ∣ i = s . \begin{cases} x_i' = x_i ,& i \in [1, s), \\ x_i' = x_i + 1 ,& i = 0 \mid \mid i = s. \end{cases} {xi=xi,xi=xi+1,i[1,s),i=0∣∣i=s. 这样就变成求解一个 ∀ x i ′ > 0 \forall x_i' > 0 xi>0 的不定方程解的个数, x 0 ′ + x 1 ′ + x 2 ′ + ⋯ + x s − 1 ′ + x s ′ = n − k + 2. x_0' + x_1' + x_2' + \cdots + x_{s - 1}' + x_{s}' = n - k + 2. x0+x1+x2++xs1+xs=nk+2. 由隔板法,解的个数为 ( ( n − k + 2 ) − 1 ( s + 1 ) − 1 ) = ( n − k + 1 s ) . {(n - k + 2) - 1 \choose (s + 1) - 1} = {n - k + 1 \choose s}. ((s+1)1(nk+2)1)=(snk+1). 因此最终答案为 ∑ s = 1 k ( n − k + 1 s ) ( k − 1 s − 1 ) m s ( m − 1 ) k − s . \sum\limits_{s = 1}^{k}{n - k + 1 \choose s}{k - 1 \choose s - 1}m^s(m - 1)^{k - s}. s=1k(snk+1)(s1k1)ms(m1)ks.

时间复杂度 O ( ∑ k log ⁡ k ) O(\sum k\log k) O(klogk)

C++ Code

#include <bits/stdc++.h>using i64 = long long;template<class T>
constexpr T power(T a, i64 b) {T res = 1;for (; b; b /= 2, a *= a) {if (b % 2) {res *= a;}}return res;
}
template<int P>
struct MInt {int x;constexpr MInt() : x{} {}constexpr MInt(i64 x) : x{norm(x % getMod())} {}static int Mod;constexpr static int getMod() {if (P > 0) {return P;} else {return Mod;}}constexpr static void setMod(int Mod_) {Mod = Mod_;}constexpr int norm(int x) const {if (x < 0) {x += getMod();}if (x >= getMod()) {x -= getMod();}return x;}constexpr int val() const {return x;}explicit constexpr operator int() const {return x;}constexpr MInt operator-() const {MInt res;res.x = norm(getMod() - x);return res;}constexpr MInt inv() const {assert(x != 0);return power(*this, getMod() - 2);}constexpr MInt &operator*=(MInt rhs) & {x = 1LL * x * rhs.x % getMod();return *this;}constexpr MInt &operator+=(MInt rhs) & {x = norm(x + rhs.x);return *this;}constexpr MInt &operator-=(MInt rhs) & {x = norm(x - rhs.x);return *this;}constexpr MInt &operator/=(MInt rhs) & {return *this *= rhs.inv();}friend constexpr MInt operator*(MInt lhs, MInt rhs) {MInt res = lhs;res *= rhs;return res;}friend constexpr MInt operator+(MInt lhs, MInt rhs) {MInt res = lhs;res += rhs;return res;}friend constexpr MInt operator-(MInt lhs, MInt rhs) {MInt res = lhs;res -= rhs;return res;}friend constexpr MInt operator/(MInt lhs, MInt rhs) {MInt res = lhs;res /= rhs;return res;}friend constexpr std::istream &operator>>(std::istream &is, MInt &a) {i64 v;is >> v;a = MInt(v);return is;}friend constexpr std::ostream &operator<<(std::ostream &os, const MInt &a) {return os << a.val();}friend constexpr bool operator==(MInt lhs, MInt rhs) {return lhs.val() == rhs.val();}friend constexpr bool operator!=(MInt lhs, MInt rhs) {return lhs.val() != rhs.val();}
};template<>
int MInt<0>::Mod = 998244353;template<int V, int P>
constexpr MInt<P> CInv = MInt<P>(V).inv();constexpr int P = 1000000007;
using Z = MInt<P>;struct Comb {int n;std::vector<Z> _fac;std::vector<Z> _invfac;std::vector<Z> _inv;Comb() : n{0}, _fac{1}, _invfac{1}, _inv{0} {}Comb(int n) : Comb() {init(n);}void init(int m) {m = std::min(m, Z::getMod() - 1);if (m <= n) return;_fac.resize(m + 1);_invfac.resize(m + 1);_inv.resize(m + 1);for (int i = n + 1; i <= m; i += 1) {_fac[i] = _fac[i - 1] * i;}_invfac[m] = _fac[m].inv();for (int i = m; i > n; i -= 1) {_invfac[i - 1] = _invfac[i] * i;_inv[i] = _invfac[i] * _fac[i - 1];}n = m;}Z fac(int m) {if (m > n) init(2 * m);return _fac[m];}Z invfac(int m) {if (m > n) init(2 * m);return _invfac[m];}Z inv(int m) {if (m > n) init(2 * m);return _inv[m];}Z binom(int n, int m) {if (n < m || m < 0) {return 0;}return fac(n) * invfac(m) * invfac(n - m);}Z Lucas(i64 n, i64 m, int p) {if (n < p and m < p) {return binom(n, m);}return Lucas(n / p, m / p, p) * binom(n % p, m % p);}Z Lucas(i64 n, i64 m) {if (n < Z::getMod() and m < Z::getMod()) {return binom(n, m);}return Lucas(n / Z::getMod(), m / Z::getMod()) * binom(n % Z::getMod(), m % Z::getMod());   }Z perm(int n, int m) {if (n < m or m < 0) {return 0;}return fac(n) * invfac(n - m);}
} comb;void solve() {int n, m, k;std::cin >> n >> m >> k;Z ans = 0;for (int s = 1; s <= k; s++) {Z res = comb.binom(n - k + 1, s) * comb.binom(k - 1, s - 1);res *= power(Z(m), s) * power(Z(m - 1), k - s);ans += res;}std::cout << ans << "\n";
}int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);int T = 1;std::cin >> T;while (T--) {solve();}return 0;
}
http://www.whsansanxincailiao.cn/news/32026944.html

相关文章:

  • 微信小程序怎么制作音乐小程序/河北搜索引擎优化
  • 网站建设ppt模板/软文营销的优势
  • 设计网站推荐什么主题/网络推广免费网站
  • 长葛网站建设公司/百度极速版推广员怎么申请
  • 深圳做网站企业/关键词在线听
  • 自适应网站主要用什么做/提升seo排名
  • 天津网站制作建设/免费注册域名网站
  • 淘宝导购网站模板/公司网站建设公司好
  • 门户网站开发视频/搜索引擎优化的简称
  • 百度博客网站模板/网络营销的方法
  • 模板下载后怎么使用/百度seo优化收费标准
  • 公司网站建设哪家快/全国教育培训机构平台
  • 网站开发深圳/合肥seo管理
  • 回忆网站模板/如何优化推广网站
  • 南宁市网站/免费的网络推广平台
  • 挂机宝可以做网站吗/seo关键词是什么意思
  • 网站备案审核过规定时间了/抖音seo点击软件排名
  • 山东网站建设公司排名/百度seo排名360
  • 兴远建设网站/百度一下官网
  • 北京学会网站建设/国际新闻头条最新消息
  • 小企业网站建设怎么做好/信息发布
  • 主题资源网站建设步骤/自己在家怎么做电商
  • 微信小程序开发工具pc6/seo必备工具
  • 网站服务费做啥费用/seo怎么优化网站排名
  • 国内外优秀设计网站/识万物扫一扫
  • dede 网站地图 模块/企业网络营销方案策划
  • 天津网站推广外包/竞价排名营销
  • 双十一网站怎么做/推广竞价账户托管
  • 自助下单网站咋做/网站制作平台
  • 电脑课做网站的作业/南昌seo技术外包