CSUACM2024新生赛 - 第2场 题解

写在前面

比赛地址:https://www.luogu.com.cn/contest/213194

难度大跳水!(大概

描述了获奖条件以及奖品的字符串为:本次新生赛的第1名,以及排名大于等于3小于10的,截止新生赛开始前,获得积分题的积分数量最多的同学,将获得ICPC2024杭州的纪念背包一个

A 线段树套平衡树

官方题解

诈骗题,在题目背景也有加粗提示注意数据范围。

对于 1 操作直接修改当前数组即可。

对于其余操作复制当前数组对应区间并排序即可轻松实现。

复杂度 O(qnlogn)

当然有能力的同学可以逝逝线段树套平衡树。

有趣的是我的队友lmx给我验数据时,真的弄了个树套树,然而在过于小的范围下树套树对比暴力的时间并没有快多少。

复制复制
// Coded by hjxddl
#include <bits/stdc++.h>//普通暴力写法
#define ll long long
#define db double
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n + 5);
for (int i = 1; i <= n; i++)
std::cin >> a[i];
int q;
std::cin >> q;
while (q--) {
int opt;
std::cin >> opt;
if (opt == 1) {
int k, x;
std::cin >> k >> x;
a[k] = x;//对于1操作直接修改
continue;
}
int k, l, r;
std::cin >> k >> l >> r;
std::vector<int> b{0};
for (int i = l; i <= r; i++)
b.push_back(a[i]);
int m = b.size();
std::sort(b.begin() + 1, b.begin() + m);//复制一份数组并排序
b.push_back(1e9 + 7);
if (opt == 2)//2操作直接输出即可
std::cout << b[k] << '\n';
else {
int rank;
for (int i = 1; i <= m; i++) {
if (k > b[i - 1] && k <= b[i]) {
rank = i;//找出k的排名,可以使用二分
break;
}
}
if (opt == 3) {
std::cout << rank << '\n';
}
if (opt == 4) {
if (rank == 1)
std::cout << (int)1e9 + 7 << '\n';
else
std::cout << b[rank - 1] << '\n';
}
if (opt == 5) {
while (b[rank] == b[rank + 1])
rank++;
if (rank == m)
std::cout << (int)1e9 + 7 << '\n';
else if (b[rank] == k)//找出k的排名时,b[rank]可能等于k或第一个比k大的数
std::cout << b[rank + 1] << '\n';//因此要对后继进行特判.
else
std::cout << b[rank] << '\n';
}
}
}
}
int main() {
std::ios::sync_with_stdio(0);
std::cin.tie(0), std::cout.tie(0);
int t=1;
// std::cin>>t;
while(t--){
solve();
}
std::cout << std::flush;
system("pause");
}

O(qn) 做法

因为这是一道允许各种方法过的签到,所以出题人写的题解有点潦草。注意一下实现即可做到所有询问的时间复杂度均为线性级别。

众所周知 STL 中有 nth_element 函数,可以在线性时间复杂度内求得给定数组的第 k 小值并将其放到数组的下标为 k 的位置上,同时将前 k 小值放在其左侧,其他值放在其右侧(但不保证这些值之间是有序的)。

则对于操作 2,直接调用 nth_element 求第 k 小即可;

对于操作 3,枚举求小于给定值的数的数量即可;

对于操作 4,枚举求小于给定值的数的最大值即可;

对于操作 5,枚举求大于给定值的数的最小值即可。

单次询问时间复杂度均为线性级别。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 1010;
const int kInf = 1000000007;
//=============================================================
int n, q, a[kN];
std::vector<int> b;
//=============================================================
//=============================================================
int main() {
//freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
std::cin >> n;
for (int i = 1; i <= n; ++ i) std::cin >> a[i];
std::cin >> q;
while (q --) {
int opt; std::cin >> opt;
if (opt == 1) {
int x, k; std::cin >> x >> k;
a[x] = k;
} else if (opt == 2) {
int k, l, r; std::cin >> k >> l >> r;
b.clear();
for (int i = l; i <= r; ++ i) b.push_back(a[i]);
std::nth_element(b.begin(), b.begin() + k - 1, b.end());
std::cout << b[k - 1] << "\n";
} else if (opt == 3) {
int k, l, r, rk = 1; std::cin >> k >> l >> r;
for (int i = l; i <= r; ++ i) if (a[i] < k) ++ rk;
std::cout << rk << "\n";
} else if (opt == 4) {
int k, l, r, ans = -kInf; std::cin >> k >> l >> r;
for (int i = l; i <= r; ++ i) if (a[i] < k && a[i] > ans) ans = a[i];
if (ans == -kInf) ans = kInf;
std::cout << ans << "\n";
} else {
int k, l, r, ans = kInf; std::cin >> k >> l >> r;
for (int i = l; i <= r; ++ i) if (a[i] > k && a[i] < ans) ans = a[i];
std::cout << ans << "\n";
}
}
return 0;
}

线段树套平衡树

#include <algorithm>
#include <iostream>
#include <fstream>
#include <cstring>
#include <cstdlib>
#include <complex>
#include <string>
#include <cstdio>
#include <vector>
#include <bitset>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <map>
#include <set>
using namespace std;
const int MAXN = 1e5 + 100;
int n, m;
int a[MAXN];
namespace Treap
{
struct balanced
{
int w;
int sz;
int num;
int fix;
int ch[2];
};
int tot;
balanced tree[MAXN * 20];
int newnode(int w)
{
++tot;
tree[tot].w = w;
tree[tot].fix = rand();
tree[tot].num = 1;
tree[tot].ch[0] = tree[tot].ch[1] = 0;
tree[tot].sz = 1;
return tot;
}
void pushup(int p)
{
tree[p].sz = tree[tree[p].ch[0]].sz + tree[tree[p].ch[1]].sz + tree[p].num;
}
void rotate(int& p, int d)
{
int y = tree[p].ch[d];
tree[p].ch[d] = tree[y].ch[d ^ 1];
tree[y].ch[d ^ 1] = p;
pushup(p);
pushup(y);
p = y;
}
void insert(int& p, int w)
{
if (!p)
p = newnode(w);
else if (tree[p].w == w)
++tree[p].num;
else
{
if (tree[p].w > w)
{
insert(tree[p].ch[0], w);
if (tree[tree[p].ch[0]].fix > tree[p].fix)
rotate(p, 0);
}
else
{
insert(tree[p].ch[1], w);
if (tree[tree[p].ch[1]].fix > tree[p].fix)
rotate(p, 1);
}
}
pushup(p);
}
void remove(int& p, int w)
{
if (tree[p].w > w)
remove(tree[p].ch[0], w);
else if (tree[p].w < w)
remove(tree[p].ch[1], w);
else
{
if (tree[p].num > 1)
--tree[p].num;
else
{
if (!tree[p].ch[0] && !tree[p].ch[1])
p = 0;
else if (!tree[p].ch[0])
{
rotate(p, 1);
remove(tree[p].ch[0], w);
}
else if (!tree[p].ch[1])
{
rotate(p, 0);
remove(tree[p].ch[1], w);
}
else
{
if (tree[tree[p].ch[0]].fix > tree[tree[p].ch[1]].fix)
{
rotate(p, 0);
remove(tree[p].ch[1], w);
}
else
{
rotate(p, 1);
remove(tree[p].ch[0], w);
}
}
}
}
if (p)
pushup(p);
}
int queryrank(int p, int k) // return the highest rank of value 'k'
{
if (!p)
return 0;
if (tree[p].w > k)
return queryrank(tree[p].ch[0], k);
else if (tree[p].w == k)
return tree[tree[p].ch[0]].sz;
else
return tree[tree[p].ch[0]].sz + tree[p].num + queryrank(tree[p].ch[1], k);
}
int querynum(int p, int k) // return the value of kth rank node
{
if (tree[tree[p].ch[0]].sz + 1 == k)
return tree[p].w;
else if (tree[tree[p].ch[0]].sz + 1 < k)
return querynum(tree[p].ch[1], k - 1 - tree[tree[p].ch[0]].sz);
else
return querynum(tree[p].ch[0], k);
}
int querypre(int p, int k) // return the prefix of value k
{
if (!p)
return -1000000007;
if (tree[p].w >= k)
return querypre(tree[p].ch[0], k);
else
return max(tree[p].w, querypre(tree[p].ch[1], k));
}
int querysuf(int p, int k) // return the suffix of value k
{
if (!p)
return 1000000007;
if (tree[p].w <= k)
return querysuf(tree[p].ch[1], k);
else
return min(tree[p].w, querysuf(tree[p].ch[0], k));
}
void listall(int p)
{
if (tree[p].ch[0])
listall(tree[p].ch[0]);
cerr << tree[p].w << ",sz=" << tree[p].num << " ";
if (tree[p].ch[1])
listall(tree[p].ch[1]);
}
}
using Treap::listall;
namespace SEG
{
struct segment
{
int l;
int r;
int root;
};
segment tree[MAXN * 8];
void build(int p, int l, int r)
{
tree[p].l = l;
tree[p].r = r;
for (int i = l; i < r + 1; ++i)
Treap::insert(tree[p].root, a[i]);
if (l != r)
{
int mid = (l + r) / 2;
build(p * 2, l, mid);
build(p * 2 + 1, mid + 1, r);
}
}
void modify(int p, int x, int y)
{
Treap::remove(tree[p].root, a[x]);
Treap::insert(tree[p].root, y);
if (tree[p].l == tree[p].r)
return;
int mid = (tree[p].l + tree[p].r) / 2;
if (x > mid)
modify(p * 2 + 1, x, y);
else
modify(p * 2, x, y);
}
int queryrank(int p, int l, int r, int k) // query the highest rank of value 'k'
{
if (tree[p].l > r || tree[p].r < l)
return 0;
if (tree[p].l >= l && tree[p].r <= r)
return Treap::queryrank(tree[p].root, k);
else
return queryrank(p * 2, l, r, k) + queryrank(p * 2 + 1, l, r, k);
}
int querynum(int u, int v, int k) // query the value of kth num
{
int l = 0, r = 1e8;
while (l < r)
{
int mid = (l + r + 1) / 2;
if (queryrank(1, u, v, mid) < k)
l = mid;
else
r = mid - 1;
}
return r;
}
int querypre(int p, int l, int r, int k)
{
if (tree[p].l > r || tree[p].r < l)
return -1000000007;
if (tree[p].l >= l && tree[p].r <= r)
return Treap::querypre(tree[p].root, k);
else
return max(querypre(p * 2, l, r, k), querypre(p * 2 + 1, l, r, k));
}
int querysuf(int p, int l, int r, int k)
{
if (tree[p].l > r || tree[p].r < l)
return 1000000007;
if (tree[p].l >= l && tree[p].r <= r)
return Treap::querysuf(tree[p].root, k);
else
return min(querysuf(p * 2, l, r, k), querysuf(p * 2 + 1, l, r, k));
}
}
int read()
{
char ch = getchar();
int x = 0, flag = 1;
while (ch != '-' && (ch < '0' || ch > '9'))
ch = getchar();
if (ch == '-')
{
ch = getchar();
flag = -1;
}
while (ch >= '0' && ch <= '9')
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * flag;
}
int main()
{
n = read();
for (int i = 1; i < n + 1; ++i)
a[i] = read();
SEG::build(1, 1, n);
m = read();
for (int i = 0; i < m; ++i)
{
int opt = read();
if (opt == 1)
{
int x = read(), y = read();
SEG::modify(1, x, y);
a[x] = y;
}
else
{
int k = read(), l = read(), r = read();
if (opt == 3)
printf("%d\n", SEG::queryrank(1, l, r, k) + 1);
else if (opt == 2)
printf("%d\n", SEG::querynum(l, r, k));
else if (opt == 4)
printf("%d\n", abs(SEG::querypre(1, l, r, k)));
else
printf("%d\n", abs(SEG::querysuf(1, l, r, k)));
}
}
return 0;
}

B 我是一个一个一个一个签到题

发现在进行异或运算时,被操作的两个数的各个二进制位可以看做相互独立的。于是考虑对 a,b,c 拆位并依次尝试最小化答案的每一位,从而最小化最终的答案。

容易发现对于某一二进制位,当且仅当 a,b,c 该位上全为 1 或全为 0 时,答案中该位上可以取 0,否则最优情况下只能取 1。

于是直接枚举各位并构造答案即可,总时间复杂度 O(Tlogv) 级别。

/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
//=============================================================
//=============================================================
int main() {
//freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
int T; std::cin >> T;
while (T --) {
int a, b, c, ans = 0; std::cin >> a >> b >> c;
for (int i = 0; i <= 30; ++ i) {
if ((a >> i & 1) && (b >> i & 1) && (c >> i & 1)) continue;
if (!(a >> i & 1) && !(b >> i & 1) && !(c >> i & 1)) continue;
ans += (1 << i);
}
std::cout << ans << "\n";
}
return 0;
}

C 不要再读假题了

是一个前缀和优化计数问题。

考虑暴力枚举每一行的 [cc] 并用 sum 数组记录下来对应的位置。

再考虑暴力枚举每一行的 [pc] , ans 加上对应 sum 数组记录下来对应的位置的值。

每次枚举下一行时对 sum 都不需要清空刚好进行了前缀和优化。

所以总复杂度为 O(n3)

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
char mp[409][409];
int sum[409][409];
signed main() {
int n, m;
cin >> n >> m;
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= m;j++) {
cin >> mp[i][j];
}
}
int ans = 0;
for (int i = 1;i <= n;i++) {
for (int l = 1;l <= m;l++) {
for (int r = l + 1;r <= m;r++) {
if (mp[i][l] == 'p' && mp[i][r] == 'c') {
ans += sum[l][r];
}
}
}
for (int l = 1;l <= m;l++) {
for (int r = l + 1;r <= m;r++) {
if (mp[i][l] == 'c' && mp[i][r] == 'c') {
sum[l][r]++;
}
}
}
}
cout << ans << endl;
return 0;
}

D A+B+C+D Problem

于是我们考虑优化,不难想到利用二分优化,我们可以进行两次二分。

第一次是思想上的,就是把 A+B+C+D=0 ,转化为 (A+B)=C+D

第二次是真正的二分,我们先用 sum1 存下来 ai+bj ​的所有情况,用 sum2 存下来 ci+dj ​的所有情况。枚举 sum1 中的每一个值,用二分查找在sum2 中找所有满足和为 0 的数的个数,求和再输出就可以啦。

std 使用了STL,map来简化代码实现。

STL map 写法

#include <bits/stdc++.h>
//#define int long long
#define endl '\n'
using namespace std;
int a[5][1009];
map<int, int>z;
signed main(){
int n;
cin >> n;
for (int i = 1;i <= 4;i++) {
for (int j = 1;j <= n;j++) {
cin >> a[i][j];
}
}
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= n;j++) {
z[a[1][i] + a[2][j]]++;
}
}
int ans = 0;
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= n;j++) {
ans += z[-a[3][i] - a[4][j]];
}
}
cout << ans << endl;
return 0;
}

二分写法

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 1e3 + 10;
//=============================================================
int n, a[kN], b[kN];
std::vector<int> c1, c2;
std::map<int, int> cnt1, cnt2;
//=============================================================
//=============================================================
int main() {
//freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
std::cin >> n;
for (int i = 1; i <= n; ++ i) std::cin >> a[i];
for (int i = 1; i <= n; ++ i) {
std::cin >> b[i];
for (int j = 1; j <= n; ++ j) {
c1.push_back(b[i] + a[j]);
}
}
for (int i = 1; i <= n; ++ i) std::cin >> a[i];
for (int i = 1; i <= n; ++ i) {
std::cin >> b[i];
for (int j = 1; j <= n; ++ j) {
c2.push_back(b[i] + a[j]);
}
}
std::sort(c1.begin(), c1.end());
std::sort(c2.begin(), c2.end());
LL ans = 0;
for (auto v: c1) {
auto it1 = std::lower_bound(c2.begin(), c2.end(), -v);
if (*it1 != -v) continue;
auto it2 = std::lower_bound(c2.begin(), c2.end(), -v + 1);
ans += it2 - it1;
}
std::cout << ans << '\n';
return 0;
}

E 喜欢加强数据是吧

题解

n>4n 为合数时,n 的两个因子必然存在于 1n1 中(因子 k 相同时,k2k 必然存在于 1n1 中)所以此时答案为 YES。

n 为质数时,很显然 1n1 不会出现 n 所以此时答案为 NO。

注意 n=4 需要特判。

证明见下。

于是仅需判断输入的数是否为 4,已经是否为素数即可。数据范围较小,可以每组数据均 O(n) 地进行判断。

顺带一提 std 写的是 Miller-Rabin 算法,本质上是一种随机化算法,可以在 O(klog3n) 的时间复杂度下快速判断出一个数是否是素数,但具有一定的错误概率。不过在一定数据范围内,通过一些技巧可以使其不出错。因为太变态了所以不放在这里了,感兴趣的同学可以自行学习。

#include <bits/stdc++.h>
using namespace std;
void solve()
{
int n; cin >> n;
if (n == 1) {
cout << "YES" << endl;
}
else if (n == 4){
cout << "NO" << endl;
}
else {
bool x = 0;
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
x = 1; break;
}
}
if (x) cout << "YES" << endl;
else cout << "NO" << endl;
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--)
solve();
}
/*
5
1 2 4 6 20
*/

证明

n 是素数时, 显然 NO.

n>4n 不是素数时, 存在 a,b[2,n1] 使得 a×b=n

  1. ab , 有:

(n1)!=a×b×=n×

  1. a=b , 则 2a<n, 于是有

(n1)!=a×2a×=n×

然后只剩下 n=4 的情况, 手玩即可。


PS:这就是威尔逊定理:

威尔逊定理 —— 数论四大定理之一 - 知乎
对于 nN+

(n1)!modn={1(p is a prime)2(p=4)0otherwise

F 努力の江队

题意分析

本题的数列为 $a_1=1,\ a_i\ge1(1\le i\le t),\quad \frac{a_{k+1}}{a_k}\in\left{
1,21k<x 1,12xk<t \right. $

其中,题目提供 m 条情报,每条情报提供 api=2qi(2pit,0qi109) 的信息。

最后,题目要求根据已有信息,构造出数列在满足给定条件下可能达到的最大值 R,输出 log2R

题目解法

判断情报真假

题目中,有默认的 a1=1=20 信息,记得将其补充。然后将情报数据 {pi,qi}pi 从小到大排序。

根据题意,数列先是经历持平或倍增的不下降的第一阶段,然后再经历持平或减半的不上升的第二阶段。因此,当数列开始出现减小后,就无法再上升。于是,若给定情报出现减小后又增大的情况,则情报虚假。

此外,在给定的两组情报之间,两者的数值差距必须能被两者的距离容忍。若情报为 a2=2,a4=32 时,a3 没有给定。此时,a3 可以等于 a22a2。因此 a3=2,4,而 a4=2,4,8,最大为 8,故本情报虚假。第二阶段的不上升区间同理。于是,当 |qi+1qi|>pi+1pi 时,情报虚假。

综上,当满足以下条件之一,则情报虚假:

  • 出现减小后又增大的情况;
  • 存在 |qi+1qi|>pi+1pi
构造可能达到的最大值

首先,最终的答案值一定不会小于情报给定值的最大值。
因此,我们只需要关注情报给定的最大值,研究其周围的空位是否能让我们填入尽量大的数值。

在如图所示的数列中,给定的最大值是 8,共有三个(用红色字体标注)。于是,我们需要在 8 相邻的未知区域(用浅红底色填充)填数,构造最大值。

在左边的 14 之间的问号,与最大值 8 无关,无需关注。在红底填充的四段区域中,第一个 8 与第二个 8 中间隔着三个空,是空隙最大的。可以填充为 {8,16,32,16,8},于是最大值为 32,输出值为 log232=5

代码

由于数列元素只会比上一个元素增大两倍或减半,且最小为 1,则数列每个元素都是二的若干整数次方 2x。又因为题目要求输出数列最大值的对数,于是我们不需要记录数列原始值,只需要记录其幂值即可。

如数列 {1,2,4,8,8,4},记录为 {0,1,2,3,3,2}。这样能够保证数据能够被正常存储不越界。

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N = 1e5+6;
LL t,m;
struct D{
LL p,q;
}dat[N];
bool cmp(D a,D b){
return a.p<b.p;
}
int main(){
ios::sync_with_stdio(false);
cin>>t>>m;
LL mx=0;
dat[0].p=1,dat[0].q=0; // a[1]=2^0=1
for(LL i=1;i<=m;++i){
cin>>dat[i].p>>dat[i].q;
mx=max(mx,dat[i].q);
}
sort(dat+1,dat+1+m,cmp);
LL down=0, res=mx;
for(LL i=1;i<=m;++i){
LL df=abs(dat[i].q-dat[i-1].q), len=dat[i].p-dat[i-1].p;
if(df>len){
cout<<"I love Yuhan\n";
return 0;
}
if(dat[i].q>dat[i-1].q){
if(down){
cout<<"I love Yuhan\n";
return 0;
}
}
if(dat[i].q<dat[i-1].q){
down=dat[i].p; // 记录开始下降
}
if(dat[i].q==mx){
len-=df; // 将左右两数持平后再计算(类似倍增时的思路)
LL r=dat[i].q+len/2;
res=max(res,r);
}
else if(dat[i-1].q==mx){
len-=df; // 同上
LL r=dat[i-1].q+len/2;
res=max(res,r);
}
}
if(!down){ // 若最后一个情报也是最大值,补充判断
res=max(res,dat[m].q+(t-dat[m].p-1));
}
cout<<res<<'\n';
}

G 我是一个动态规划?

分析

答案具有单调性。如果无法在 k 次修改内做到 ,那么肯定也无法在 k 次修改内做到。考虑二分答案。

如何判断当前答案 x 是否可行?考虑 DP

状态:

dpi​ 表示前 i 个元素中,不修改 i 这个元素的条件下,使数列符合要求的最小修改次数。

答案:

由于不确定保留哪个元素更优,所以枚举保留位置 i,取 min((ni)+dpi)。即取前 i 个数的最优方案,然后将后面的数全部修改。

你可能想问:将后面所有数全改了肯定不优啊?

因为 i 后面的数不归 dpi​ 管,等我们枚举到后面的 dp 值,它会帮我们保留后面的数的。这样就可以知道后面的数如果不全部更改,是否更优了。还不懂的话可以拿样例三试一试。

状态转移:

首先枚举当前状态 i,我们考虑枚举最后修改的那一段位置(可以不修改)。所以枚举上一个决策 j(上一个保留的位置),[j+1,i1] 就是最后一段修改的位置。显然 i 和 j 都不能修改。但如果 aiaj∣>(ij)×x 的话,i 和 j 必须修改,所以我们要判断 j 是否合法。

确定了 i 和 j,还是一样的,前 j 个数取最优方案,保留 j。然后将 j+1 到 i1 这一段全部修改,最后保留 i

dpi=min(dpi,dpj+(ij1))

初始化:

把 [1,i1] 这一段全修改了一定能达成目标。

如果对于一个 i,没有合法的 j,那么也只能这样。

初始化即 dpi=i1

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N = 2005;
int n, k, dp[N], a[N];
bool check(int x){
for (int i = 1; i <= n; i++) {
dp[i] = i - 1;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j < i; j++) {
if (abs(a[i] - a[j]) <= (i - j) * x) {
dp[i] = min(dp[i], dp[j] + i - j - 1);
}
}
}
for (int i = 1; i <= n; i++) {
if (n - i + dp[i] <= k) {
return 1;
}
}
return 0;
}
signed main(){
cin >> n >> k;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
int l = -1, r = 2e9 + 1;
while (l + 1 < r){
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid;
}
cout << r << endl;
return 0;
}

写在最后

看完了第七卷没有败犬女主看我要死了啊啊啊

posted @   Luckyblock  阅读(130)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
点击右上角即可分享
微信分享提示