蓝桥杯省赛题目选解/模板整理

[蓝桥杯 2022 省 A] 最长不下降子序列(Tag:dp,树状数组,离散化)

题意

可以修改最多连续 \(k\) 个数为同一个数,求LIS长度。\(10^5\)

题解

考虑最优答案一定是两段LIS中间一段 \(k\) 个相同的数拼接而成(这 \(k\) 个数也可能在开头/结尾)。

分别求出以 \(i\) 开头和结尾的 \(LIS\) 长度\(g[i],f[i]\)

最后求解 \(\max (g[i] + k + \max\limits_{j < i - k, a[j] \le a[i]}{f[j]})\)即可

//
// Created by blackbird on 2023/4/4.
//
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;

int n, k, a[N], f[N], g[N];

struct Fenwick_Tree {   //树状数组维护前缀最大值
    int c[N];
    int add(int i, int x) {
        for (; i <= n; i += i & -i)
            c[i] = max(c[i], x);
    }
    int ask(int i) {
        int res = 0;
        for (; i; i -= i & -i)
            res = max(res, c[i]);
        return res;
    }
} F, G, T;

int work() { //离散化
    vector<int> vec;
    for (int i = 1; i <= n; i++) vec.push_back(a[i]);
    sort(vec.begin(), vec.end());
    vec.erase(unique(vec.begin(), vec.end()), vec.end());
    for (int i = 1; i <= n; i++) a[i] = lower_bound(vec.begin(), vec.end(), a[i]) - vec.begin() + 1;
}



int main() {
    cin >> n >> k;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    work();
    a[0] = 1; a[n + 1] = n + 1;//注意:树状数组不能用下标0

    for (int i = 1; i <= n; i++) {
        f[i] = F.ask(a[i]) + 1;
        F.add(a[i], f[i]);
    }

    for (int i = n; i >= 1; i--) {
        g[i] = G.ask(n - a[i] + 1) + 1;
        G.add(n - a[i] + 1, g[i]);
    }
    int ans = 0;
    for (int i = k + 1; i <= n + 1; i++) {
        T.add(a[i - k - 1], f[i - k - 1]);
        ans = max(ans, T.ask(a[i]) + k + g[i]);
    }
    cout << ans << "\n";
    return 0;
}

[蓝桥杯 2013 省 B] 连号区间数 (Tag: 线段树区间修改,单调栈)

题意

给定一个排列,求满足\(\max\limits_{l\le i\le r} p_i-\min\limits_{l\le i\le r} p_i=r-l\)的子区间数量。\(5\times 10^4\)

题解

\(f_l=\max-\min + l-r\),显然 \(f\) 的最小值是 \(0\)

考虑右端点 \(r\)\(1\)\(n\) 移动,用线段树维护区间 \([1,r]\)\(f_l\) ,并支持查询最小值出现的数量。

维护 \(\max\)\(\min\) 时,用单调栈确定当前的 \(p_r\) 依次影响了哪些区间,然后进行区间加即可。

//
// Created by blackbird on 2023/4/4.
//
#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N = 2e5 + 10;
int n, a[N];

struct tree {
    int min, add, cnt;
} tr[N << 2];

#define mid (l+r>>1)
#define ls (u<<1)
#define rs (u<<1|1)

void pushup(int u) {
    tr[u].min = min(tr[ls].min, tr[rs].min);
    tr[u].cnt = 0;
    if (tr[ls].min == tr[u].min) tr[u].cnt += tr[ls].cnt;
    if (tr[rs].min == tr[u].min) tr[u].cnt += tr[rs].cnt;
}
void pushdown(int u) {
    if (!tr[u].add) return;
    tr[ls].min += tr[u].add; tr[ls].add += tr[u].add;
    tr[rs].min += tr[u].add; tr[rs].add += tr[u].add;
    tr[u].add = 0;
}

void build(int l, int r, int u) {
    if (l == r) {
        tr[u].cnt = 1;
        return;
    }
    build(l, mid, ls); build(mid + 1, r, rs);
    pushup(u);
}

void add(int s, int t, int l, int r, int u, int k) {
    if (s > t) return;
    if (s <= l && t >= r) {
        tr[u].min += k;
        tr[u].add += k;
        return;
    }
    pushdown(u);
    if (s <= mid) add(s, t, l, mid, ls, k);
    if (t >= mid + 1) add(s, t, mid + 1, r, rs, k);
    pushup(u);
}

int query_cnt(int s, int t, int l, int r, int u) {
    if (s > t) return 0;
    if (s <= l && t >= r) {
        return tr[u].min == 0 ? tr[u].cnt : 0;
    }
    pushdown(u);
    int res = 0;
    if (s <= mid) res += query_cnt(s, t, l, mid, ls);
    if (t >= mid + 1) res += query_cnt(s, t, mid + 1, r, rs);
    return res;
}

signed main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    build(1, n, 1);

    int ans = 0;
    stack<int> mx, mn;//单调减/增栈
    for (int i = 1; i <= n; i++) {
        // mx - mn + l - r
        // r : += 1
        add(1, i - 1, 1, n, 1, -1);
        // mx: a[tp] -> a[i]
        while (!mx.empty() && a[mx.top()] <= a[i]) {
            int tp = mx.top(); mx.pop();
            int pre = mx.empty() ? 0 : mx.top();
            add(pre + 1, tp, 1, n, 1, a[i] - a[tp]);
        }
        mx.push(i);
        // mn: a[tp] -> a[i]
        while (!mn.empty() && a[mn.top()] >= a[i]) {
            int tp = mn.top(); mn.pop();
            int pre = mn.empty() ? 0 : mn.top();
            add(pre + 1, tp, 1, n, 1, a[tp] - a[i]);
        }
        mn.push(i);
        ans += query_cnt(1, i, 1, n, 1);
    }
    cout << ans << "\n";
    return 0;
}

[BOI2009]Radio Transmission 无线传输(Tag:KMP)

题意

求字符串的最小循环节长度

题解

n - next[n]。只是复习kmp。
见https://www.cnblogs.com/vv123/p/16390472.html

//
// Created by blackbird on 2023/4/5.
//
#include <bits/stdc++.h>
using namespace std;

const int N = 2e6 + 10;
int n, m, f[N];
char p[N], s[N];

int main() {
    cin >> n >> p + 1;
    for (int i = 2, j = 0; i <= n; i++) {
        while (j && p[j + 1] != p[i]) j = f[j];
        if (p[j + 1] == p[i]) j++;
        f[i] = j;
    }
    cout << n - f[n] << endl;
    /*
    int cnt = 0;
    cin >> m >> s + 1;
    for (int i = 1, j = 0; i <= m; i++) {
        while (j && p[j + 1] != s[i]) j = f[j];
        if (p[j + 1] == s[i]) j++;
        if (j == n) cnt++, j = f[j];
    }
    cout << cnt << endl;
     */
    return 0;
}


[蓝桥杯 2022 国 B] 出差 (Tag:Dijkstra)

题意

给定有边权无向图,从每个城市离开有隔离时间(时代的眼泪),求最短路。

题解

隔离时间加进边权即可。
只是复习Dijkstra。

//
// Created by blackbird on 2023/4/5.
//
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int,int>

const int N = 2e5 + 10, inf = 1e9;
struct edge { int v, w; };
vector<edge> g[N];
int n, m, c[N], d[N], vis[N];

void dijkstra() {
    for (int i = 1; i <= n; i++) d[i] = inf;
    d[1] = 0;
    priority_queue<pii, vector<pii>, greater<pii> > q;
    q.push({0, 1});
    while (!q.empty()) {
        int u = q.top().second; q.pop();
        if (vis[u]) continue; vis[u] = 1;
        for (auto e : g[u]) {
            int v = e.v, w = e.w;
            if (d[u] + w < d[v]) {
                d[v] = d[u] + w;
                q.push({d[v], v});
            }
        }
    }
}

signed main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> c[i];
    c[1] = 0;
    for (int i = 1; i <= m; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        g[u].push_back({v, w + c[u]});
        g[v].push_back({u, w + c[v]});
    }
    dijkstra();
    cout << d[n] << endl;
    return 0;
}

[蓝桥杯 2022 国 B] 机房 (Tag:LCA,树上前缀和)

题意

点权=度数,求树上两点的最短路径。

题解

//
// Created by blackbird on 2023/4/5.
//
#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N = 2e6 + 10;
int n, m, a[N], sum[N];
vector<int> g[N];


int d[N], f[N][31];

void dfs(int u, int fa) {
    d[u] = d[fa] + 1;
    f[u][0] = fa;
    sum[u] = sum[fa] + a[u];
    for (int i = 1; (1 << i) <= d[u]; i++) {
        f[u][i] = f[f[u][i - 1]][i - 1];
    }
    for (auto v : g[u]) {
        if (v != fa) dfs(v, u);
    }
}

int lca(int u, int v) {
    if (d[u] < d[v]) swap(u, v);
    int k = d[u] - d[v];
    for (int i = 29; i >= 0; i--) {
        if (k >> i & 1) u = f[u][i];
    }
    if (u == v) return u;
    for (int i = 29; i >= 0; i--) {
        if (f[u][i] != f[v][i])
            u = f[u][i], v = f[v][i];
    }
    return f[u][0];
}



signed main() {
    cin >> n >> m;
    for (int i = 1; i <= n - 1; i++) {
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
        a[u]++; a[v]++;
    }

    dfs(1, 0);

    for (int i = 1; i <= m; i++) {
        int u, v;
        cin >> u >> v;
        int t = lca(u, v);
        cout << sum[u] + sum[v] - 2 * sum[t] + a[t] << "\n";
    }

    return 0;
}

二分

useful algorithm

在单增序列中查找x或x的后继

while (l < r) {  
    int mid = l + r >> 1;  
    if (a[mid] >= x) r = mid; else l = mid + 1;  
}  
return a[l];  

在单增序列中查找x或x的前驱

while (l < r) {  
    int mid = l + r + 1 >> 1;  
    if (a[mid] <= x) l = mid; else r = mid - 1;  
}  
return a[l];  

Trie

(万一考了呢)

#include <iostream>
#include <cstdio>
using namespace std;
 
const int maxn = 2e5 + 10;
 
int son[maxn][26], cnt[maxn], idx, n;
char op[2], s[maxn];
 
void Insert(char *str) {
  int p = 0;
  for (int i = 0; str[i]; i++) {
    int u = str[i] - 'a';
    if (!son[p][u]) son[p][u] = ++idx;
    p = son[p][u];
  }
  cnt[p]++;
}
 
int Query(char *str) {
  int p = 0;
  for (int i = 0; str[i]; i++) {
    int u = str[i] - 'a';
    if (!son[p][u]) return 0;
    p = son[p][u];
  }
  return cnt[p];
}
 
 
int main() {
  scanf("%d", &n);
  while (n--) {
    scanf("%s%s", op, s);
    if (op[0] == 'I') Insert(s);
    else printf("%d\n", Query(s));
  }
  return 0;
}

线性筛

//
// Created by vv123 on 2022/7/8.
//
#include <bits/stdc++.h>
#define int long long
using namespace std;
 
const int N = 500000 + 10;
int n;
int vis[N], prime[N], phi[N], mu[N], c[N], d[N], g[N], f[N], F[N], G[N], gg[N];
 
void Shai() {
    vis[1] = phi[1] = mu[1] = d[1] = g[1] = f[1] = G[1] = gg[1] = F[1] = 1;
    int cnt = 0;
    for (int i = 2; i <= n; i++) {
        if (!vis[i]) {
            prime[++cnt] = i;
            phi[i] = i - 1;
            mu[i] = -1;
            c[i] = 1; //质数的最小质因子指数为1
            d[i] = c[i] + 1;   //质数的约数个数为2。d[n] = PI(c_i + 1)
            g[i] = i + 1;//g[i]为i的最小质因子0~k次方和,约数和f[i]为所有k次质因子g[i]的积
            gg[i] = i;
            f[i] = g[i];
            G[i] = mu[1] * phi[1] * f[i] + mu[i] * phi[i] * f[1];
            F[i] = G[i];
        }
        for (int j = 1; j <= cnt && i * prime[j] <= n; j++) {
            //printf("i=%d prime[j]=%d\n", i, prime[j]);
            vis[i * prime[j]] = 1;
            if (i % prime[j] == 0) {
                //printf("%d mod %d == 0\n", i, prime[j]);
                phi[i * prime[j]] = prime[j] * phi[i];  //n'包含了n的所有质因子,与n的欧拉函数只差p1
                mu[i * prime[j]] = 0; //p1的指数大于1,莫比乌斯函数为0
                c[i * prime[j]] = c[i] + 1; //注意到i和i * prime[j]的最小质因子都是prime[j],后面都很显然了
                d[i * prime[j]] = d[i] / (c[i] + 1) * (c[i * prime[j]] + 1);
                g[i * prime[j]] = g[i] * prime[j] + 1;
                gg[i * prime[j]] = gg[i] * prime[j];
                f[i * prime[j]] = f[i] / g[i] * g[i * prime[j]];
                G[i * prime[j]] = G[i] + gg[i];         //
                F[i * prime[j]] = F[i] / G[i] * G[i * prime[j]];
 
                break;
            } else {
                //printf("%d mod %d == %d\n", i, prime[j], i % prime[j]);
                phi[i * prime[j]] = phi[prime[j]] * phi[i]; //最小质因子不整除其他部分,n'和p1互质
                mu[i * prime[j]] = mu[prime[j]] * mu[i];
                c[i * prime[j]] = 1;
                d[i * prime[j]] = d[prime[j]] * d[i];
                g[i * prime[j]] = 1 + prime[j];
                gg[i * prime[j]] = prime[j];
                f[i * prime[j]] = f[prime[j]] * f[i];
                G[i * prime[j]] = G[prime[j]];
                F[i * prime[j]] = F[prime[j]] * F[i];
            }
        }
    }
}
 
void solve() {
    cin >> n;
    Shai();
    for (int i = 1; i <= n; i++) printf("%d ", phi[i]); puts("");
    for (int i = 1; i <= n; i++) printf("%d ", mu[i]); puts("");
    for (int i = 1; i <= n; i++) printf("%d ", d[i]); puts("");
    for (int i = 1; i <= n; i++) printf("%d ", f[i]); puts("");
    //for (int i = 1; i <= n; i++) printf("%d ", gg[i]); puts("");
    //for (int i = 1; i <= n; i++) printf("%d ", G[i]); puts("");
    for (int i = 1; i <= n; i++) printf("%d ", F[i]); puts("");
}
 
signed main() {
    int T = 1;
    //cin >> T;
    while (T--) {
        solve();
    }
    return 0;
}

[蓝桥杯 2019 省 A] RSA (Tag: exgcd,快速幂)

//求正整数e,使得d * e % (p-1)(q-1) = 1
void exgcd(int a, int b, int &x, int &y) {
    if (b == 0) {
        x = 1, y = 0;
        return;
    }
    exgcd(b, a % b, y, x);
    y -= a / b * x;
}

void E() {
    int n = 1001733993063167141, c = 20190324, d = 212353;
    int p = 891234941, q = 1123984201, e, k;
    int a = d, b = (p - 1) * (q - 1);
    exgcd(a,  b, e, k);
    //d * e % (p-1)(q-1) = 1
    int te = e;
    e = (e % b + b) % b;
    k += (te - e) / b * a;
    //cout << qpow(c, e, n) << endl;
}
def qpow(a, b, p):
    res = 1
    while b != 0:
        if b & 1:
            res = res * a % p
        a = a * a % p
        b = b // 2
    return res

n = 1001733993063167141
c = 20190324
e = 823816093931522017
k = 174637
p = 891234941
q = 1123984201
d = 212353

print(qpow(c, e, n))

[蓝桥杯 2019 省 A] 修改数组 (Tag: 并查集,路径压缩)

//
// Created by blackbird on 2023/4/7.
//
#include <bits/stdc++.h>
#define int long long
#define pii pair<int,int>
using namespace std;

const int N = 2e6 + 10, inf = 1e9;
int n, a[N], f[N];

int find(int x) {
    return x == f[x] ? f[x] : f[x] = find(f[x]);
}

signed main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    for (int i = 1; i <= n; i++) {
        if (!f[a[i]]) f[a[i]] = a[i];
        else {
            int rt = find(a[i]);
            while (f[rt + 1]) {
                rt = f[rt] = find(rt + 1);
            }
            a[i] = rt + 1;
            f[a[i]] = a[i];
        }
    }
    for (int i = 1; i <= n; i++)
        cout << a[i] << " ";
    cout << "\n";
    return 0;
}

[蓝桥杯 2019 省 A] 糖果 (Tag:状压DP)

//
// Created by blackbird on 2023/4/7.
//
#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N = 2e6 + 10, inf = 1e9;
int n, m, k, a[N], dp[N];

signed main() {
    cin >> n >> m >> k;
    for (int i = 0; i <= (1 << m) - 1; i++) {
        dp[i] = inf;
    }
    dp[0] = 0;
    for (int i = 1; i <= n; i++) {
        int x;
        for (int j = 1; j <= k; j++) {
            cin >> x;
            a[i] |= (1 << (x - 1));
        }
        dp[a[i]] = 1;
    }

    for (int i = 0; i <= (1 << m) - 1; i++) {
        if (dp[i] == inf) continue;
        for (int j = 1; j <= n; j++) {
            if (dp[i | a[j]] > dp[i] + 1)
                dp[i | a[j]] = dp[i] + 1;
        }
    }

    if (dp[(1 << m) - 1] == inf) cout << "-1\n";
    else cout << dp[(1 << m) - 1] << "\n";
    return 0;
}

upd:
image

upd:
这也能省一?

posted @ 2023-04-04 22:23  _vv123  阅读(115)  评论(0编辑  收藏  举报