9.7 - 9.10 的一些测试
#
标签(空格分隔): 解题报告
---
## 六校联考
---
A
给出 $n$ 个数 $a_i$,对于每个数求其他有多少数是该数的倍数
$n <= 100000, a_i <= 1000000$
Solve1:
对于每个数枚举约数,那么对每个数答案的贡献就是约数出现的次数
出现的次数可以预处理
时间复杂度 $O(na_{i}^{\frac{1}{2}})$
Solve2:
类似筛法
对于每个数 $a_i$, 它只会对 $ka_i$ 的答案产生贡献
当然只需要枚举所有不同的 $a_i$
时间复杂度是调和级数也就是 $O(nlogn)$
B
给出长度为 $n$ 的字符串,最大化区间字母出现最多次数 - 字母出现最少次数,最少次数要不为 $0$
对于任意两个字母 $a, b$,
数组 $sum[a, i]$ 表示字母 $a$ 到 $i$ 的次数的前缀和
答案可以表示成 $sum[a, i] - sum[a, j] - (sum[b, i] - sum[b, j])$
等价于 $sum[a, i] - sum[b, i] - (sum[a, j] - sum[b, j])$
记录 $sum[a, j] - sum[b, j]$ 的最小值
防止出现空
维护最大值和次大值
时间复杂度 $O(26n)$
C
一张图,对于每个点 $t$, 删除掉从 $1$ 出发的到 $t$ 的最短路上的最后一条边后求到 $1$ 的最短路
建出最短路树后同bzoj3694
线段树维护树链剖分
时间复杂度 $O(nlogn * logn)$
D
给出两个字符串 $a, b$, 允许 $k$ 次修改将任意串的任意字符修改为任意字符求最长连续相同子串
长度 $300$
由于连续,所以枚举从 $a, b$ 开始匹配的位置,每遇到不同就修改一次,此过程中取最大值
时间复杂度 $O(n ^ 3)$
E
F
---
## nowcoder 提高组(第一场)
---
A
给定长度为 $n$ 序列 $a$,求长度 $>= len$ 的区间的最大中位数
$n <= 1e5$
二分答案 $x$, Check 当前序列中是否存在满足条件的中位数比 $x$ 大
Check: 若 $a[i] >= x$, 则 $a[i] = 1$, 否则 $a[i] = -1$
做前缀和
这样处理出来之后,若一段区间的和为正数,则说明存在更优的中位数
#include <bits/stdc++.h> using namespace std; const int maxn = 100005; int a[maxn], n, L; bool test(int z) { static int b[maxn]; for(int i = 1;i <= n;i ++) if (a[i] >= z) b[i] = 1; else b[i] = -1; for(int i = 1, mi = (1 << 30);i <= n;i ++) { if (i >= L) mi = min(mi, b[i - L]); b[i] += b[i - 1]; if (i >= L && b[i] - mi > 0) return 1; } return 0; } int main() { scanf("%d%d", &n, &L); for(int i = 1;i <= n;i ++) scanf("%d", &a[i]); int l = 0, r = int(1e9), tmp; for(int mid;l <= r;) { mid = l + r >> 1; if (test(mid)) tmp = mid, l = mid + 1; else r = mid - 1; } printf("%d\n", tmp); return 0; }
B
数位 dp
C
一棵树,存在 $m$ 条覆盖路径覆盖这棵树,对于每个点 $x$, 求出深度最小的点 $y$ 使得 $x -> y$ 这段路径被至少 $k$ 条覆盖路径完全覆盖,注意是完全覆盖。
由于是完全覆盖,所以这 $k$ 条路径都经过了 $x -> y$ 这条链
问题转化:树上存在某些点可以向上跳,查询相当于查询 $x$ 的子树中的所有向上跳的高度的 $k$ 的值
主席树 + dfs序
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <string> #include <vector> using namespace std; const int N = 2e5 + 10; #define LL long long #define gc getchar() inline int read() { int x = 0; char c = gc; while(c < '0' || c > '9') c = gc; while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = gc; return x; } struct Node_1{ int u, v, nxt; }G[N << 2]; int topp[N], fa[N], size[N], son[N], deep[N], tree[N], bef[N], data[N], Tree, lst[N], rst[N]; int now, head[N]; int n, m; int X[N], Y[N], lca[N]; int Root[N]; void Add(int u, int v) {G[++ now].v = v; G[now].nxt = head[u]; head[u] = now;} void Dfs_1(int u, int f_, int dep) { fa[u] = f_, deep[u] = dep, size[u] = 1; for(int i = head[u]; ~ i; i = G[i].nxt) { int v = G[i].v; if(v == f_) continue; Dfs_1(v, u, dep + 1); size[u] += size[v]; if(size[son[u]] < size[v]) son[u] = v; } } void Dfs_2(int u, int tp) { topp[u] = tp, tree[u] = ++ Tree, lst[u] = Tree, bef[Tree] = u;; if(!son[u]) {rst[u] = Tree; return ;} Dfs_2(son[u], tp); for(int i = head[u]; ~ i; i = G[i].nxt) { int v = G[i].v; if(v != fa[u] && v != son[u]) Dfs_2(v, v); } rst[u] = Tree; } inline int Lca(int x, int y) { int tpx = topp[x], tpy = topp[y]; while(tpx != tpy){ if(deep[tpx] < deep[tpy]) swap(x, y), swap(tpx, tpy); x = fa[tpx], tpx = topp[x]; } return deep[x] < deep[y] ? x : y; } int Lson[N * 65], Rson[N * 65], W[N * 65]; void Fill(int x, int y) { Lson[x] = Lson[y], Rson[x] = Rson[y], W[x] = W[y]; } int Segjs; void Insert(int &rt, int l, int r, int x) { Fill(++ Segjs, rt); rt = Segjs; W[rt] ++; if(l == r) return ; int mid = (l + r) >> 1; if(x <= mid) Insert(Lson[rt], l, mid, x); else Insert(Rson[rt], mid + 1, r, x); } int Seg(int lrt, int rrt, int l, int r, int k) { if(l == r) { if(k > (W[rrt] - W[lrt])) return (1 << 30); return l; } int ll = Lson[lrt], rr = Lson[rrt]; int num = W[Lson[rrt]] - W[Lson[lrt]]; int mid = (l + r) >> 1; if(num >= k) return Seg(Lson[lrt], Lson[rrt], l, mid, k); else return Seg(Rson[lrt], Rson[rrt], mid + 1, r, k - num); } int b; vector <int> Data[N]; int main() { n = read(), m = read(); for(int i = 1; i <= n; i ++) head[i] = -1; for(int i = 1; i < n; i ++) { int u = read(), v = read(); Add(u, v), Add(v, u); } Dfs_1(1, 0, 1); Dfs_2(1, 1); for(int i = 1; i <= m; i ++) { X[i] = read(), Y[i] = read(); lca[i] = Lca(X[i], Y[i]); } for(int i = 1; i <= m; i ++) { Data[X[i]].push_back(deep[lca[i]]); Data[Y[i]].push_back(deep[lca[i]]); } for(int i = 1; i <= n; i ++) { Root[i] = Root[i - 1]; b = bef[i]; int S = Data[b].size(); if(S == 0) continue; for(int j = 0; j < S; j ++) { int x = Data[b][j]; Insert(Root[i], 1, n, x); } } int q = read(); for(int i = 1; i <= q; i ++) { int x = read(), k = read(); int a = Seg(Root[lst[x] - 1], Root[rst[x]], 1, n, k); if(a >= deep[x]) printf("0\n"); else printf("%d\n", deep[x] - a); } return 0; }
---
## zhengruioj Day3
---
T1
给出长度 $<= n$ 的数组 $a$, $a_i != a_j, a_i <= n$
求长度为 $n$ 的排列使得数组 $a$ 的元素相对位置不变,最小化排列的字典序。
从 $1$ 开始枚举不在 $a$ 中的数,每次与 $a$ 数组的尾元素比较,输出较小的数并且删除 $a$ 的尾元素
T2
采矿点升序排序, $a_i$ 表示第 $i$ 个采矿点能采到的矿石种类数,$b_i$ 表示第 $i$ 个采矿点第一次能采到的矿石种类数。显然 $ans = \sum_{i = 1} ^ {m} 2 ^ {b_i} * 2 ^ {a_i - b_i}$
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <cstring> #include <string> using namespace std; const int N = 1e5 + 10; int A[N], B[N], Use[N]; struct Node {int L, R;} C[N]; int n, m; #define LL long long const int Mod = 998244353; inline LL Ksm(LL a, LL b) { LL ret = 1; while(b) { if(b & 1) ret = ret * a % Mod; a = a * a % Mod; b >>= 1; } return ret; } int main() { cin >> n >> m; for(int i = 1; i <= n; i ++) cin >> C[i].L >> C[i].R; for(int i = 1; i <= m; i ++) cin >> Use[i]; sort(Use + 1, Use + m + 1); for(int i = 1; i <= n; i ++) { int Wei1 = lower_bound(Use + 1, Use + m + 1, C[i].L) - Use; int Wei2 = lower_bound(Use + 1, Use + m + 1, C[i].R) - Use; if(C[i].R < Use[1] || C[i].L > Use[m]) continue; if(Wei1 == Wei2 && Use[Wei2] > C[i].R) continue; if((Use[Wei2] > C[i].R) || (Wei2 > m)) Wei2 --; A[Wei1] ++, A[Wei2 + 1] --; B[Wei1] ++; } for(int i = 1; i <= m; i ++) A[i] += A[i - 1]; LL Answer = 0; for(int i = 1; i <= m; i ++) if(B[i]) (Answer += ((Ksm((LL)2, (LL)B[i]) - 1) * Ksm((LL)2, (LL)A[i] - (LL)B[i])) % Mod) %= Mod; cout << Answer; return 0; }
T3