蓝桥杯省赛题目选解/模板整理
[蓝桥杯 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:
upd:
这也能省一?