smu2024蓝桥杯训练3
A
思路:维护第一组出现的数即可
查看代码
void solve() {
int n, m, ans = 0;
set<int> se;
cin >> n >> m;
for (int i = 0, x; i < n; ++i) cin >> x, se.insert(x);
for (int i = 0, x; i < m; ++i) {
cin >> x;
if(se.count(x)) ans ++;
}
cout << ans;
}
B
思路:可以看到n的范围并不大,枚举所有人,每满m个单价加v
查看代码
void solve() {
int ans = 0, n, v, m, a;
cin >> n >> v >> m >> a;
for (int i = 1; i <= n; ++i) {
ans += v;
if (i % m == 0) v += a;
}
cout << ans;
}
C
思路:求距离可以求出两个数所在的位置,位置差的和即为距离。可以发现奇数行是正序,偶数行是逆序,根据行数再求出列数即可
查看代码
void solve() {
int w, m, n;
cin >> w >> m >> n;
auto P = [w](int x) {
int h = (x + w - 1) / w, l;
if (h % 2) {
l = x % w;
if (l == 0) l = w;
} else {
l = w - x % w + 1;
if (l == w + 1) l = 1;
}
PII ans = {h, l};
return ans;
};
PII a = P(m), b = P(n);
cout << abs(a.first - b.first) + abs(a.second - b.second);
}
D
思路:由于子串中一种字符只能存在一个,且一个字符在一个子串中的贡献为1。那就统计每个字符带来的贡献,预处理出一个位置的前后再次出现该字符的位置,统计与前后的间隔l和r,这个位置上的字符带来的贡献即为l * r,这样的复杂度是On的
查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int, int>
#define PDI pair<double, int>
const double eps = 1e-12;
const int N = 1e4 + 5;
const int dx[4] = {0,-1}, dy[4] = {-1, 0};
const int INF = 0x3f3f3f3f;
void solve() {
string s;
cin >> s;
int n = s.size();
s = ' ' + s;
vector<int> vel(n + 5);
vector<int> ver(n + 5);
vector<int> pos(30, n + 1);
for (int i = n; i >= 1; --i) {
ver[i] = pos[s[i] - 'a'];
pos[s[i] - 'a'] = i;
}
pos = vector<int> (30, 0);
for (int i = 1; i <= n; ++i) {
vel[i]= pos[s[i] - 'a'];
pos[s[i] - 'a'] = i;
}
int ans = 0;
for (int i = 1; i <= n; ++i) {
int a = i - vel[i];
int b = ver[i] - i;
ans += a * b;
}
cout << ans;
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
}
E
思路:dp,之前写过这道啦
根据xxxxx = xxx00 + xx,以这种形式看一个数。对于xx只需要找到符合条件的xxx00(x可以是多位,0的个数为xx的个数)满足%k等于0。
用f[i][j]表示a[ ]*10i % k = j 的数的个数,统计每个数的xxx00个数
查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int, int>
#define PDI pair<double, int>
const double eps = 1e-12;
const int N = 1e4 + 5;
const int dx[4] = {0,-1}, dy[4] = {-1, 0};
const int INF = 0x3f3f3f3f;
void solve() {
int n, k;
cin >> n >> k;
vector<int> a(n + 1), cnt(n + 1);
vector f(10, vector<int> (k));
for (int i = 1; i <= n; ++i) {
cin >> a[i];
for (int j = 1, x = a[i]; j <= 9; ++j) {
x *= 10;
f[j][x % k] ++;
}
int x = a[i], c = 0;
while (x > 0) {
c ++;
x /= 10;
}
cnt[i] = c;
}
int ans = 0;
for (int i = 1; i <= n; ++i) {
int c = (k - (a[i] % k)) % k;
ans += f[cnt[i]][c];
c = (a[i] + (int)(a[i] * pow(10, cnt[i]))) % k;
if (c == 0) ans --;
}
cout << ans;
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
}
F
思路:dp,题目说了要在T内用完M力量,f[i][j]表示在i秒内用了j力量没有死掉的方案数
状态为:第i秒用1力量:f[i][j] += f[i-1][j-1]
第i秒什么都不做:f[i][j] += f[i-1][j]
这里需要满足D - (i - j)+ j > 0
查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int, int>
#define PDI pair<double, int>
const double eps = 1e-12;
const int N = 1e4 + 5;
const int dx[4] = {0, -1}, dy[4] = {-1, 0};
const int INF = 0x3f3f3f3f, Mod = 1e9 + 7;
void solve() {
int D, T, M;
cin >> D >> T >> M;
vector f(T + 1, vector<int> (M + 1));
f[0][0] = 1;
for (int i = 1; i <= T; ++i) {
for (int j = 0; j <= min(i, M); ++j) {
if (D - i + 2 * j <= 0) continue;
if (j) f[i][j] = (f[i][j] + f[i - 1][j - 1]) % Mod;
f[i][j] = (f[i][j] + f[i - 1][j]) % Mod;
}
}
cout << f[T][M];
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
}
H
思路:dp,首先f[i][j]表示S的前i个包含T的前j个的最少操作次数
若S[i] = T[j],f[i][j] = f[i - 1][j - 1]
若S[i] != T[j], f[i][j] = min (f[i - 1][j - 1] + 1,f[i - 1][j]), 分别表示对S[i]进行修改、不对S[i]操作即S的前i - 1要包含T的前j个
查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int, int>
#define PDI pair<double, int>
const double eps = 1e-12;
const int N = 1e4 + 5;
const int dx[4] = {0,-1}, dy[4] = {-1, 0};
const int INF = 0x3f3f3f3f;
void solve() {
string S, T;
cin >> S >> T;
int n, m;
n = S.size(), m = T.size();
S = ' ' + S, T = ' ' + T;
vector f(n + 1, vector<int> (m + 1, INF));
for (int i = 0; i <= n; ++i) f[i][0] = 0;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= i && j <= m; ++j) {
if (S[i] == T[j]) f[i][j] = f[i - 1][j - 1];
else f[i][j] = min(f[i - 1][j - 1] + 1, f[i - 1][j]);
}
}
cout << f[n][m];
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
}
I
思路:算是暴力过的吧,首先用并查集合并,当添加权值时,对该集合的父亲进行标记,当合并时,首先将所有点先进行权值增加,增加后将父亲的标记清空,再合并
查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int, int>
#define PDI pair<double, int>
const double eps = 1e-12;
const int N = 1e6 + 5;
const int dx[4] = {0, -1}, dy[4] = {-1, 0};
const int INF = 0x3f3f3f3f;
int n, m;
int fa[N];
int find(int x) {
if (x != fa[x]) fa[x] = find(fa[x]);
return fa[x];
}
void mer(int a, int b) {
a = find(a), b = find(b);
if (a != b) fa[b] = a;
}
void solve() {
cin >> n >> m;
vector<int> ans(n + 1), c(n + 1, 0);
for (int i = 1; i <= n; ++i) fa[i] = i;
for (int i = 0; i < m; ++i) {
int op, a, b;
cin >> op >> a >> b;
if (op == 1) {
if (find(a) != find(b)) {
for (int i = 1; i <= n; ++i) {
ans[i] += c[find(i)];
}
fill(c.begin(), c.end(), 0);
mer(a, b);
}
} else {
c[find(a)] += b;
}
}
for (int i = 1; i <= n; ++i) cout << ans[i] + c[find(i)] << ' ';
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
}
看了别的解法树上差分+并查集,首先还是再添加权值时,只对父亲进行标记,当合并时,新增一个节点,让两个集合的父亲的父亲都为该新增节点,这样如果再进行添加权值时,不会影响到原先集合的添加权值,最后dfs所有新增节点,往下添加权值
查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int, int>
#define PDI pair<double, int>
const double eps = 1e-12;
const int N = 1e5 + 5;
const int dx[4] = {0,-1}, dy[4] = {-1, 0};
const int INF = 0x3f3f3f3f;
int n, m, idx;
int fa[N], val[N];
int find(int x) {
if (x != fa[x]) fa[x] = find(fa[x]);
return fa[x];
}
vector<vector<int> > ve(N);
void dfs(int u,int v) {
// int vv = v + val[u];
// ans[u] += vv;
val[u] += v;
for (auto a: ve[u]) {
dfs(a, val[u]);
}
}
void solve() {
cin >> n >> m;
idx = n + 1;
for (int i = 1; i <= n; ++i) fa[i] = i;
for (int i = 0; i < m; ++i) {
int op, a, b;
cin >> op >> a >> b;
if (op == 1) {
a = find(a), b = find(b);
if (a != b) {
fa[a] = idx, fa[b] = idx, fa[idx] = idx;
ve[idx].push_back(a), ve[idx].push_back(b);
idx ++;
}
} else {
val[find(a)] += b;
}
}
for (int i = 1; i < idx; ++i)
if (i == find(i)) dfs(i, 0);
for (int i = 1; i <= n; ++i) cout << val[i] << ' ';
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
}
J
思路:啊分层最短路还是做的少,没想到
由于要选两条路出来,那就多建2层图出来,层之间为选的一条路,有3层说明选了两条路,一层内还是按原图的可行路建图,注意层与层之间是单向边,最后跑最短路即可
查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int, int>
#define PDI pair<double, int>
const double eps = 1e-12;
const int N = 1e4 + 5;
const int dx[4] = {0,-1}, dy[4] = {-1, 0};
const int INF = 0x3f3f3f3f;
struct E{
int v, w;
};
void solve() {
int n, m;
cin >> n >> m;
vector<vector<E> > ve(3 * n + 5);
vector<int> dis(3 * n + 5, INF);
auto add = [&](int a, int b, int c) {
ve[a].push_back({b, c});
};
for (int i = 0; i < m; ++i) {
int a, b, c, d;
cin >> a >> b >> c >> d;
if (d == 1) {
add(a, b + n, c), add(b, a + n, c);
add(a + n, b + 2 * n, c), add(b + n, a + 2 * n, c);
} else {
add(a, b, c), add(b, a, c);
add(a + n, b + n, c), add(b + n, a + n, c);
add(a + 2 * n, b + 2 * n, c), add(b + 2 * n, a + 2 * n, c);
}
}
dis[1] = 0;
priority_queue<PII, vector<PII>, greater<PII> > q;
vector<int> st(3 * n + 5);
q.push({dis[1], 1});
while (!q.empty()) {
auto [d, u] = q.top();
q.pop();
if (st[u]) continue;
st[u] = 1;
for (auto [v, w]:ve[u]) {
if (dis[v] > d + w) {
dis[v] = d + w;
q.push({dis[v], v});
}
}
}
cout << dis[n] - min({dis[n], dis[n + n], dis[n + n + n]});
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
}