2025牛客寒假算法基础集训营6
A. 复制鸡
题意:对于一个字符串,你每次可以选择其中任意一些位置,然后把每个位置上的字符复制一个到这个位置后面。现在给你一个操作过的字符串,求它的所有可能的原串的最小长度。
因为每个位置操作后是在后面添加一个相同的字符,那么我们可以把所有相同的区间当做一个字符。数有多少个这样的区间就行了。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
int ans = 1;
for (int i = 1; i < n; ++ i) {
ans += a[i] != a[i - 1];
}
std::cout << ans << "\n";
}
B. 好伙计猜拳
题意:给你
比较简单的
点击查看代码
void solve() {
int n, c1, c2;
std::cin >> n >> c1 >> c2;
std::vector<std::pair<int, int>> a(n + 2);
for (int i = 1; i <= n; ++ i) {
std::cin >> a[i].first >> a[i].second;
}
a[n + 1] = {2e9, 2e9};
const i64 inf = 1e18;
std::vector f(n + 2, std::array<i64, 2>{inf, inf});
f[0][0] = 0;
for (int i = 1; i <= n + 1; ++ i) {
for (int j = 0; j < i ; ++ j) {
auto & [x, y] = a[i];
auto & [c, d] = a[j];
if (c <= x && d <= y) {
f[i][0] = std::min(f[i][0], f[j][0] + (i64)(i - j - 1) * c1);
}
if (d <= x && c <= y) {
f[i][0] = std::min(f[i][0], f[j][1] + (i64)(i - j - 1) * c1);
}
if (c <= y && d <= x) {
f[i][1] = std::min(f[i][1], f[j][0] + (i64)(i - j - 1) * c1 + c2);
}
if (c <= x && d <= y) {
f[i][1] = std::min(f[i][1], f[j][1] + (i64)(i - j - 1) * c1 + c2);
}
}
}
std::cout << f[n + 1][0] << "\n";
}
C. 数列之和
题意:在一个由偶数组成的无穷序列里,求所有长度大于
点击查看代码
void solve() {
//(j - i + 1)(i + j - 2)
i64 k;
std::cin >> k;
auto check = [&](i64 x) -> i64 {
i64 lg = std::__lg(x) - 1;
return x / 2 - lg;
};
i64 l = 2, r = 3e18;
while (l < r) {
i64 mid = l + r >> 1ll;
if (check(mid) >= k) {
r = mid;
} else {
l = mid + 1;
}
}
std::cout << l << "\n";
}
D. Viva La Vida(Fried-Chicken's Version)
题意:有一个
首先,最后的图一定是一个森林,然后森林的联通块的个数就是点数减边数,因为森林可以从一棵树删边变过来,一棵树是
E. 任造化落骰
题意:给你
注意考虑独立均匀生成带来了什么性质。结论是所有区间的
那么可以枚举左端点,每次求一段
点击查看代码
template <class Info>
struct ST {
std::vector<std::vector<Info>> st;
ST(std::vector<Info> a) {
int n = a.size(), m = std::__lg(n) + 1;
st.assign(n, std::vector<Info>(m));
for (int i = 0; i < n; ++ i) {
st[i][0] = a[i];
}
for (int j = 1; j < m; ++ j) {
for (int i = 0; i + (1 << j - 1) < n; ++ i) {
st[i][j] = st[i][j - 1] + st[i + (1 << j - 1)][j - 1];
}
}
}
Info query(int l, int r) {
int lg = std::__lg(r - l + 1);
return st[l][lg] + st[r - (1 << lg) + 1][lg];
}
};
struct Info {
int max, min;
};
Info operator + (Info a, Info b) {
return {std::max(a.max, b.max), std::min(a.min, b.min)};
}
bool operator == (const Info a, const Info b) {
return a.max == b.max && a.min == b.min;
}
void solve() {
int n, m, q;
std::cin >> n >> m >> q;
std::vector<int> a(n);
std::vector<Info> info(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
info[i] = {a[i], a[i]};
}
ST<Info> st(info);
std::map<i64, i64> mp;
for (int i = 0; i < n; ++ i) {
int j = i;
while (j < n) {
Info info = st.query(i, j);
int l = j, r = n - 1;
while (l < r) {
int mid = l + r + 1 >> 1;
if (st.query(i, mid) == info) {
l = mid;
} else {
r = mid - 1;
}
}
mp[(i64)info.max * info.min] += l - j + 1;
j = l + 1;
}
}
std::vector<i64> v;
for (auto & [x, y] : mp) {
v.push_back(x);
}
int cnt = v.size();
std::vector<i64> sum(cnt + 1);
for (int i = 0; i < cnt; ++ i) {
sum[i + 1] = sum[i] + mp[v[i]];
}
while (q -- ) {
i64 k;
std::cin >> k;
int p = std::lower_bound(v.begin(), v.end(), k) - v.begin();
std::cout << sum[cnt] - sum[p] << "\n";
}
}
F. 薪得体会
题意:
发现如果我们选最大的
按照
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<std::pair<int, int>> a(n + 1);
for (int i = 1; i <= n; ++ i) {
std::cin >> a[i].first >> a[i].second;
}
std::sort(a.begin() + 1, a.end(), [&](std::pair<int, int> & a, std::pair<int, int> & b) {
return a.first < b.first;
});
std::vector<int> pre(n + 1);
for (int i = 1; i <= n; ++ i) {
pre[i] = std::max(pre[i - 1], a[i].first + a[i].second);
}
int ans = 0;
for (int i = 1; i <= n; ++ i) {
int j = i;
while (j <= n && a[i].first == a[j].first) {
++ j;
}
-- j;
if (j - i + 1 > 1) {
ans = std::max(ans, pre[j]);
} else if (ans >= a[i].first) {
ans = std::max(ans, a[i].first + a[i].second);
}
ans = std::max(ans, pre[i - 1]);
i = j;
}
ans = std::max(ans, a[n].first);
std::cout << ans << "\n";
}
G. 目标是【L2】传说高手
题意:有
观察到小数位很少,为了保证精度我们可以给每个概率乘上
先看问题二,考虑最多多少场一定满足所有情况,发现
那么我们可以从小到大枚举总场次
再看问题一,首先化为最简分式的形式来看,必如
那么我们也可以枚举分母,对于每个卡牌找一个满足条件的分母记录胜场和败场的最大值即可。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::string s;
std::cin >> s;
for (int j = 0; j < s.size(); ++ j) {
if (s[j] != '.') {
a[i] = a[i] * 10 + s[j] - '0';
}
}
a[i] *= 10;
// std::cout << a[i] << " \n"[i == n - 1];
}
std::sort(a.begin(), a.end());
if (a[n - 1] == 0) {
std::cout << 0 << " " << 0 << "\n";
return;
}
int ans1 = 0, ans2 = 0;
int max1 = 0, max2 = 0;
std::vector<int> st(n);
int cnt = 0;
for (int i = 1; cnt < n; ++ i) {
for (int j = 0; j < n; ++ j) {
if (st[j]) {
continue;
}
if (a[j] == 0) {
st[j] = 1;
++ cnt;
continue;
}
int down = i * (a[j] - 5);
int up = i * (a[j] + 5);
if (down % 10000) {
down /= 10000;
down += 1;
down *= 10000;
}
if (down < up) {
++ cnt;
st[j] = 1;
int x = down / 10000;
max1 = std::max(max1, x);
max2 = std::max(max2, i - x);
}
}
}
ans1 = max1 + max2;
for (int i = 1; i <= 1000; ++ i) {
int j = 0;
for (int k = 0; k < n; ++ k) {
while (j <= i && (j * 10000 < i * (a[k] - 5) || j * 10000 >= i * (a[k] + 5))) {
++ j;
}
if (j > i) {
break;
}
}
if (j <= i) {
ans2 = i;
break;
}
}
std::cout << ans1 << " " << ans2 << "\n";
}
H. 小鸡的排列构造
题意:要求构造一个排列,使得每个数在使用区间长度是偶数的区间或者区间长度是奇数的区间排序后位置改变。
这题可以猜结论,然后用
下面是我的对拍代码。
点击查看代码
#define ls(u) tr[u].lson
#define rs(u) tr[u].rson
const int N = 3e5 + 5;
struct Node {
int lson, rson;
int sum;
}tr[N << 5];
int tot = 0;
int root[N];
void build(int &u, int l, int r) {
u = ++ tot;
if (l == r) {
return;
}
int mid = l + r >> 1;
build(ls(u), l, mid); build(rs(u), mid + 1, r);
}
void modify(int &u, int v, int l, int r, int p, int x) {
u = ++ tot;
tr[u] = tr[v];
tr[u].sum += x;
if (l == r) {
return;
}
int mid = l + r >> 1;
if (p <= mid) {
modify(ls(u), ls(v), l, mid, p, x);
} else {
modify(rs(u), rs(v), mid + 1, r, p, x);
}
}
int query(int u, int v, int l, int r, int p) {
if (r <= p) {
return tr[u].sum - tr[v].sum;
}
int mid = l + r >> 1;
if (p <= mid) {
return query(ls(u), ls(v), l, mid, p);
} else {
return query(ls(u), ls(v), l, mid, p) + query(rs(u), rs(v), mid + 1, r, p);
}
}
std::mt19937 gen(std::random_device{}());
int rand(int l, int r) {
std::uniform_int_distribution<int> dis(l, r);
return dis(gen);
}
void solve() {
// int n, m;
// std::cin >> n >> m;
int n = 2000, m = 2000;
std::vector<std::array<int, 3>> a(m);
int t = rand(0, 1);
// std::cerr << t << "\n";
for (int i = 0; i < m; ++ i) {
// int l, r, c;
// std::cin >> l >> r >> c;
int l = rand(1, n - 2), r = rand(l + 1, n);
while ((r - l) % 2 != t) {
r = rand(l + 1, n);
}
int c = rand(l, r);
a[i] = {l, r, c};
}
std::vector<int> ans(n);
for (int i = 0; i < n; ++ i) {
ans[i] = n - i;
}
if ((a[0][1] - a[0][0] + 1) & 1) {
for (int i = 1; i + 1 < n; i += 2) {
std::swap(ans[i], ans[i + 1]);
}
}
// for (int i = 0; i < n; ++ i) {
// std::cout << ans[i] << " \n"[i == n - 1];
// }
tot = 0;
build(root[0], 1, n);
for (int i = 1; i <= n; ++ i) {
modify(root[i], root[i - 1], 1, n, ans[i - 1], 1);
}
for (auto & [l, r, c] : a) {
if (c == l + query(root[r], root[l - 1], 1, n, ans[c - 1]) - 1) {
std::cout << "!!!" << l << " " << r << " " << c << "\n";
}
}
}
分类讨论一下,如果区间长度是偶数,则直接
否则如果是奇数,直接从第二个位置开始,两个两个交换就行了,直接猜的。
点击查看代码
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<std::array<int, 3>> a(m);
for (int i = 0; i < m; ++ i) {
int l, r, c;
std::cin >> l >> r >> c;
a[i] = {l, r, c};
}
std::vector<int> ans(n);
for (int i = 0; i < n; ++ i) {
ans[i] = n - i;
}
if ((a[0][1] - a[0][0] + 1) & 1) {
for (int i = 1; i + 1 < n; i += 2) {
std::swap(ans[i], ans[i + 1]);
}
}
for (int i = 0; i < n; ++ i) {
std::cout << ans[i] << " \n"[i == n - 1];
}
}
I. 小鸡的排列构造的checker
题意:一个排列中,对于
没脑子想不出正解,直接上主席树了。
用主席树维护值域,然后对于每个查询,就是求
点击查看代码
#define ls(u) tr[u].lson
#define rs(u) tr[u].rson
const int N = 3e5 + 5;
struct Node {
int lson, rson;
int sum;
}tr[N << 5];
int tot = 0;
int root[N];
void build(int &u, int l, int r) {
u = ++ tot;
if (l == r) {
return;
}
int mid = l + r >> 1;
build(ls(u), l, mid); build(rs(u), mid + 1, r);
}
void modify(int &u, int v, int l, int r, int p, int x) {
u = ++ tot;
tr[u] = tr[v];
tr[u].sum += x;
if (l == r) {
return;
}
int mid = l + r >> 1;
if (p <= mid) {
modify(ls(u), ls(v), l, mid, p, x);
} else {
modify(rs(u), rs(v), mid + 1, r, p, x);
}
}
int query(int u, int v, int l, int r, int p) {
if (r <= p) {
return tr[u].sum - tr[v].sum;
}
int mid = l + r >> 1;
if (p <= mid) {
return query(ls(u), ls(v), l, mid, p);
} else {
return query(ls(u), ls(v), l, mid, p) + query(rs(u), rs(v), mid + 1, r, p);
}
}
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<int> a(n + 1);
for (int i = 1; i <= n; ++ i) {
std::cin >> a[i];
}
tot = 0;
build(root[0], 1, n);
for (int i = 1; i <= n; ++ i) {
modify(root[i], root[i - 1], 1, n, a[i], 1);
}
while (m -- ) {
int l, r, c;
std::cin >> l >> r >> c;
std::cout << l + query(root[r], root[l - 1], 1, n, a[c]) - 1 << "\n";
}
}
J. 铁刀磨成针
题意:有
如果我们先砍几刀再来磨刀,显然是不划算的,最差也是一边磨刀一边攻击。所以我们枚举前面某
点击查看代码
void solve() {
i64 n, x, y;
std::cin >> n >> x >> y;
i64 ans = 0;
for (int i = 0; i <= std::min(n, y); ++ i) {
i64 m = n - i;
i64 t = std::min(y - i, m);
i64 sum = t * (x + i + 1);
m -= t;
i64 a = x + i;
m = std::min(m, a);
sum += m * (a + a - m + 1) / 2;
ans = std::max(ans, sum);
}
std::cout << ans << "\n";
}
E. 鸡翻题
题意:
首先明显是一个奇数加一个偶数,所以
点击查看代码
void solve() {
int x, y;
std::cin >> x >> y;
if (y % 2 == 0 || y / 2 % 2 != x % 2) {
std::cout << "NO\n";
} else {
std::cout << "YES\n";
}
}
L. 变鸡器
题意:给你一个字符串,每次可以选择两个不一样的同时删去,最后能不能变成"
先检查字符够不够,然后检查CHICKEN是不是子序列。
然后减去对应的字符个数,那么问题就变成一个经典问题,也是上一场考过的知识点,有
点击查看代码
void solve() {
int n;
std::cin >> n;
std::string s;
std::cin >> s;
std::vector<int> cnt(26);
for (auto & c : s) {
++ cnt[c - 'A'];
}
std::string t = "CHICKEN";
for (auto & c : t) {
if ( -- cnt[c - 'A'] < 0) {
std::cout << "NO\n";
return;
}
}
int j = 0;
for (int i = 0; j < t.size() && i < n; ++ i) {
if (s[i] == t[j]) {
++ j;
}
}
if (j != t.size()) {
std::cout << "NO\n";
return;
}
int sum = std::accumulate(cnt.begin(), cnt.end(), 0);
int max = *std::max_element(cnt.begin(), cnt.end());
if (sum % 2 == 1 || max > sum / 2) {
std::cout << "NO\n";
} else {
std::cout << "YES\n";
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!