C S P − S 2024 模拟赛 5 补题报告 2024 年 8 月 2 日 − 2023 年 8 月 2 日 b y 邓时飏 CSP \ - \ S \ \ 2024 \ \ 模拟赛5 \ \ 补题报告 \\ 2024年8月2日 - 2023年8月2日 \\ by \ \ \ 邓时飏 CSP − S 2024 模拟赛5 补题报告2024年8月2日−2023年8月2日by 邓时飏
一、做题情况
-
第一题比赛 0 0 0 / 100 100 100 ,赛后通过
-
第二题比赛 0 0 0 / 100 100 100 ,赛后通过
-
第三题比赛 0 0 0 / 100 100 100 ,赛后通过
-
第四题比赛 0 0 0 / 100 100 100 ,赛后通过
-
比赛得分 0 0 0 / 400 400 400 ,赛后补题 400 400 400 / 400 400 400
二、题解报告
T1:
题面:
做法:
过预处理来构建 nxt 和 las 数组,然后利用这两个数组在常数时间内回答每个查询。预处理部分的时间复杂度是 O(n),每个查询的处理时间是 O(1),因此总的时间复杂度是 O(n + q),
附:AC代码
#include<bits/stdc++.h>
using namespace std;
const int N = 5000010;
int a[N], nxt[N], las[N];
int n, q;
inline int read() {
int x = 0;
bool flag = 0;
char ch = getchar();
while (!isdigit(ch)) flag = (ch == '-'), ch = getchar();
while (isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
return flag ? -x : x;
}
signed main() {
freopen("hot.in", "r", stdin);
freopen("hot.out", "w", stdout);
cin >> n >> q;
for (int i = 1; i <= n; ++i) a[i] = read();
for (int i = 1; i <= n; ++i) {
if (a[i] >= a[i - 1]) nxt[i] = max(nxt[i], nxt[i - 1] + 1);
else nxt[i] = 1;
}
for (int i = n; i >= 1; --i) {
if (a[i] >= a[i + 1]) las[i] = max(las[i], las[i + 1] + 1);
else las[i] = 1;
}
int l, r;
for (int i = 1; i <= q; ++i) {
l = read(), r = read();
if (nxt[r] + las[l] >= r - l + 1) cout << 'Y' << endl;
else cout << 'N' << endl;
}
return 0;
}
T2:
题面:
做法:
从右向左遍历细胞数组,通过不断地将每个细胞分割成较小的部分,并确保每次分割后的最大部分大小不超过当前的最小值。这样可以确保通过最少的劈砍次数使整个数组成为非递减序列。
附:AC代码
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
int a[N];
long long ans;
int c;
int main() {
freopen("divide.in", "r", stdin);
freopen("divide.out", "w", stdout);
scanf("%d", &n);
for (register int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
int mm = a[n];
for (register int i = n - 1; i > 0; --i) {
int pnum = ceil(a[i] * 1.0 / mm);
ans += pnum - 1;
mm = a[i] / pnum;
}
printf("%lld", ans);
return 0;
}
T3:
题面:
做法:
通过动态规划的方法分别从横向和纵向两个方向计算矩阵中可能的最大路径和,然后将这两个结果相加,得到最终的最大价值和。这样的设计能够有效地处理路径和的问题,同时保证计算的效率。
附:AC代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e3;
int n, m,a[N+5][N+5],dp[N+5],ans;
int main() {
freopen("tunnel.in","r",stdin);
freopen("tunnel.out","w",stdout);
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
cin >> n >> m;
for(int i=1; i<=n; i++){
for(int j=1; j<=m; j++) cin >> a[i][j];
}
for(int i=1; i<=n; i++) {
for(int j=2; j<=m; j++) dp[j] = max(dp[j-2] + max(0, a[i][j-1] + a[i][j]), dp[j-1]);
ans += dp[m];
}
dp[1] = 0;
for(int i=1; i<=m; i++) {
for(int j=2; j<=n; j++) dp[j] = max(dp[j-2] + max(0, a[j-1][i] + a[j][i]), dp[j-1]);
ans += dp[n];
}
cout << ans << "\n";
return 0;
}
T4:
题面:
做法:
在一个长度为 n 的数组 a 中,找出某种最大值(可能是最长的递增子序列或最大权值和,具体取决于题意)。代码使用了线段树来加速区间查询和更新操作。
附:AC代码
#include<bits/stdc++.h>
using namespace std;
const int Max_N=2e5;
int n,cnt,a[Max_N+5],pos[Max_N+5],nxt[Max_N+5],R[Max_N+5],rp[Max_N+5];
struct Tree{ int l,r,Mn; } A[Max_N*4+5];
vector<int> p[Max_N+5];
void Build(int x,int l,int r){
A[x].l=l; A[x].r=r; A[x].Mn=n+1;
if(l==r) return ;
Build(x<<1,l,(l+r)>>1);
Build((x<<1)|1,((l+r)>>1)+1,r);
}
int Ask(int x,int k){
if(k<=A[x].l&&k<=A[x].Mn) return A[x].r;
if(A[x].l==A[x].r) return 0;
if(k<=A[x<<1].r){
int t=Ask(x<<1,k);
if(t==A[x<<1].r){
int w=Ask((x<<1)|1,k);
if(w) return w;
else return t;
} else return t;
}
return Ask((x<<1)|1,k);
}
void Add(int x,int p,int w){
if(A[x].l==A[x].r){
A[x].Mn=w;
return;
}
if(p<=A[x<<1].r) Add(x<<1,p,w);
else Add((x<<1)|1,p,w);
A[x].Mn=min(A[x<<1].Mn,A[(x<<1)|1].Mn);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),p[a[i]].push_back(i),rp[i]=p[a[i]].size();
for(int i=n;i>=1;i--) pos[i]=n+1;
for(int i=n;i>=1;i--){
nxt[i]=pos[a[i]];
pos[a[i]]=i;
}
Build(1,1,n);
long long ans=n;
for(int i=n;i>=1;i--){
if(nxt[i]==n+1||i+1>nxt[i]-1||R[i+1]<nxt[i]-1||a[i+1]!=a[nxt[i]-1]) R[i]=i;
else {
int rans=R[nxt[i]];
rans=min(rans,Ask(1,nxt[i]));
if(rans==0) R[i]=nxt[i],++ans;
else {
int x=upper_bound(p[a[i]].begin(),p[a[i]].end(),rans)-p[a[i]].begin();
R[i]=p[a[i]][x-1]; ans+=x-rp[i];
}
}
if(nxt[i]<=n) Add(1,nxt[i],i);
}
printf("%lld\n",ans);
return 0;
}
四、赛后总结
提升实力,控制时间。