9.7

标签(空格分隔): 解题报告

---

## 六校联考

---

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
", tmp);
    return 0;
}
View Code

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
");
        else printf("%d
", deep[x] - a);
    }
    return 0;
}
View Code

---

## 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;
}
View Code

T3

原文地址:https://www.cnblogs.com/shandongs1/p/9627792.html