CF EDU-162 (已更新:A-C+D的代码)
CF-EDU-162(已更新:A-C+D的代码)
结果上看还不坏,但赛时写题过程十分若只(⊙﹏⊙)
A
写得最难绷的一次A题,赛时自己脑子简直是一团浆糊,我过的时候这题都过了一万人了,当时心都凉了……
分析
就是求1之间0的个数
操作
记录第一个1,与最后一个1,再遍历这个区间内0的个数就行了。记录首尾1可以用打两个标记的方法实现。
代码
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define db(x) cout<<x<<" "<<endl;
#define _db(a,n) for(int i=1;i<=n;i++) cout<<a[i]<<" ";cout<<endl;
#define mem(a) memset(a,0, sizeof(a))
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
const int N=2e5+5;
int a[N];
signed main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t,n,x,cnt=0,ans=0;cin>>t;
while(t--){
cin>>n;
ans=0;
int f=1,ff=0,r=1,l=1;
//f标记只更新一次,ff标记始终更新
rep(i,1,n){
cin>>a[i];
if(a[i]==1&&f){
f=0;
l=i;
}
else if(a[i]==1) r=i;
}
rep(i,l+1,r-1){
if(a[i]==0) ans++;
}
cout<<ans<<endl;
}
return 0;
}
B
分析
贪心的想,我们发射的子弹都先在距离最近的敌人上,而且每秒都用k发子弹——对于距离为i的敌人就用了i*k的子弹,也就是可以使敌人血量减少的总量为i乘k。因此只要满足每个i乘k大于等于当前最近的敌人的血量之和就行了
操作
可以将敌人的距离作下标,存对应距离的敌人血量之和p[i],再遍历距离,求前缀和,看是否都满足i*k>=p[i]
代码
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define db(x) cout<<x<<" "<<endl;
#define _db(a,n) for(int i=1;i<=n;i++) cout<<a[i]<<" ";cout<<endl;
#define mem(a) memset(a,0, sizeof(a))
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
const int N=3e5+5;
int a[N],b[N],p[N];
signed main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t,n,x,k,cnt=0,ans=0;cin>>t;
while(t--){
cin>>n>>k;
rep(i,1,n){
cin>>a[i];
b[i]=0;
p[i]=0;
}
int mi=1e9;
rep(i,1,n){
cin>>x;
x=abs(x);
mi=min(mi,x);
b[x]+=a[i];//对应距离的敌人血量之和,也就是桶排
}
int f=1;
rep(i,mi,n){
p[i]=p[i-1]+b[i];
if(i*k<p[i]){
f=0;
break;
}
}
if(f) cout<<"YES";
else cout<<"NO";
cout<<endl;
}
return 0;
}
C
赛时想到了求区间和的思路,可能是比赛前还在补线段树的题——我居然在半个小时里把线段树和树状数组都写了一次
(而且交上去wa了),然后才想到用前缀和做……
分析
题意得要想明白,特别要结合样例来看,是说对于数组a从a[l]到a[r],如果存在数组b满足区间[l,r]内:
- 和相同
- a[i]!=b[i]
- b[i]>0
由1,2可知,,,直接说结论:要保证a[i]!=b[i]且和相同,意味着我们只能转移a[i]的权值来使a[i]相对原来的值发生变化
只要权值发生变化就可以了
那具体每个a[i]是怎样变化?自然是可以加可以减,但是第3个条件使得对于权值为1的a[i],它们的值只能增加,这个增加量我们称为"必要增加量",且它只能来自其它权值不为1的a[i]的减少量,我们称为"最大减少量",因此,只要使得区间内最大减少量——(最大为)所有(a[i]-1)之和能大于等于必要增加量——(最小为)1的个数就可以了
操作
我的做法是赋值再求和,如果a[i]为1,将其赋值为-1,否则,a[i]-=1,这样一来,原本as数组中的1权值变成了-1——变成了它对区间内必要增加量的贡献,其它树的权值减去了1——变成了它对区间内最大减少量的贡献.
这样新数组的区间和只要>=0即满足条件.
代码
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define db(x) cout<<x<<" "<<endl;
#define _db(a,n) for(int i=1;i<=n;i++) cout<<a[i]<<" ";cout<<endl;
#define mem(a) memset(a,0, sizeof(a))
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
#define lc p<<1
#define rc p<<1|1
const int N=3e5+5;
int a[N],p[N];
signed main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t,x,y,q,n,cnt=0,ans=0;cin>>t;
while(t--){
cin>>n>>q;
mem(p);
rep(i,1,n){
cin>>x;
//赋值再求和
if(x==1) x-=2;
else x--;
p[i]=p[i-1]+x;
}
while(q--){
cin>>x>>y;
//如果x==y需要特判
if(x==y){
cout<<"NO"<<endl;
continue;
}
if(p[y]-p[x-1]>=0) cout<<"YES";
else cout<<"NO";
cout<<endl;
}
}
return 0;
}
D
这题调了一下午……,wa了8次,t了两次(实际上是数组开小了),就先放代码吧
分析
前缀和+二分
代码
- 错解
缺少判断二分到的区间的数值种类是不是大于等于2
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define db(x) cout<<x<<" "<<endl;
#define _db(a,n) for(int i=1;i<=n;i++) cout<<a[i]<<" ";cout<<endl;
#define mem(a) memset(a,0, sizeof(a))
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
const int N=2e5+5;
int a[N],p[N],b[N],ll[N],rr[N];
signed main()
{
//std::ios::sync_with_stdio(false);
//cin.tie(0);
//cout.tie(0);
int t,n,x,cnt=0,ans=0;cin>>t;
while(t--){
cin>>n;
rep(i,1,n){
cin>>a[i];
p[i]=0;
//b[i]=3e5;
p[i]=p[i-1]+a[i];
if(a[i]!=a[i-1]) ll[i]=i-1;
else ll[i]=ll[i-1];
}
int tp=3e5;
rep(i,1,n){
int l=1,r=i,mid,f=0;
while(r-l>1){
mid=l+r>>1;
//if(mid>ll[i-1]) r=mid;
if(p[i-1]-p[mid-1]>a[i]) l=mid;
else r=mid;
}
//cout<<l<<" "<<r<<endl;
if(p[i-1]-p[l-1]>a[i]) b[i]=i-l;
else b[i]=tp;
l=i,r=n;
while(r-l>1){
mid=l+r>>1;
if(p[mid]-p[i]<=a[i]) l=mid;
else r=mid;
}
//cout<<l<<" "<<r<<endl;
if(p[r]-p[i]>a[i]) b[i]=min(b[i],r-i);//cout<<r-i<<" ";
else b[i]=min(tp,b[i]);//,cout<<"-1 ";
}
rep(i,1,n){
if(b[i]!=tp) cout<<b[i]<<" ";
else cout<<"-1 ";
}
cout<<endl;
}
return 0;
}
犯过的错包括但不限于:试图在输入的正序循环里求需要倒序得到的rr数组、二分条件写错、少写对数组越界的特判……
//重点 //找到左边第一个与a[i]不同的数的位置 rep(i,1,n){ if(i==1) ll[i]=0; else{ if(a[i]!=a[i-1]) ll[i]=i-1; else ll[i]=ll[i-1]; } } //找到右边边第一个与a[i]不同的数的位置 per(i,n,1){ //因为没加这两个特判wa了五次(⊙﹏⊙) if(i==n) rr[i]=n+1; else{ if(a[i]!=a[i+1]) rr[i]=i+1; else rr[i]=rr[i+1]; } }
- 正解
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define db(x) cout<<x<<" "<<endl;
#define _db(a,n) for(int i=1;i<=n;i++) cout<<a[i]<<" ";cout<<endl;
#define mem(a) memset(a,0, sizeof(a))
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
const int N=3e5+5;
int a[N],p[N],b[N],ll[N],rr[N];
signed main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t,n,x,cnt=0,ans=0;cin>>t;
while(t--){
cin>>n;
rep(i,1,n){
cin>>a[i];
p[i]=0;
b[i]=3e5;
p[i]=p[i-1]+a[i];
if(i==1) ll[i]=0;
else{
if(a[i]!=a[i-1]) ll[i]=i-1;
else ll[i]=ll[i-1];
}
}
// rep(i,1,n) cout<<ll[i]<<" ";
// cout<<endl;
per(i,n,1){
//因为没加这两个特判wa了五次(⊙﹏⊙)
if(i==n) rr[i]=n+1;
else{
if(a[i]!=a[i+1]) rr[i]=i+1;
else rr[i]=rr[i+1];
}
}
// rep(i,1,n) cout<<rr[i]<<" ";
// cout<<endl;
int tp=3e5;
rep(i,1,n){
int l=1,r=i,mid,f=0;
while(r-l>1){
mid=l+r>>1;
//if(mid>ll[i-1]) r=mid;
if(p[i-1]-p[mid-1]>a[i]) l=mid;
else r=mid;
}
//cout<<l<<" "<<r<<endl;
if(i-l>1&&l>ll[i-1]) l=ll[i-1];
if(l>0&&p[i-1]-p[l-1]>a[i]) b[i]=i-l;
else b[i]=tp;
l=i,r=n;
while(r-l>1){
mid=l+r>>1;
if(p[mid]-p[i]<=a[i]) l=mid;
else r=mid;
}
//cout<<l<<" "<<r<<endl;
if(r-i>1&&r<rr[i+1]) r=rr[i+1];
if(r!=n+1&&p[r]-p[i]>a[i]) b[i]=min(b[i],r-i);//cout<<r-i<<" ";
else b[i]=min(tp,b[i]);//,cout<<"-1 ";
}
rep(i,1,n){
if(b[i]!=tp) cout<<b[i]<<" ";
else cout<<"-1 ";
}
cout<<endl;
}
return 0;
}
- 优化后
去掉了b数组,用ans输出结果
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define db(x) cout<<x<<" "<<endl;
#define _db(a,n) for(int i=1;i<=n;i++) cout<<a[i]<<" ";cout<<endl;
#define mem(a) memset(a,0, sizeof(a))
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
const int N=3e5+5;
int a[N],p[N],ll[N],rr[N];
signed main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t,n,x,cnt=0,ans=0;cin>>t;
while(t--){
cin>>n;
rep(i,1,n){
cin>>a[i];
p[i]=0;
p[i]=p[i-1]+a[i];
if(i==1) ll[i]=0;
else{
if(a[i]!=a[i-1]) ll[i]=i-1;
else ll[i]=ll[i-1];
}
}
per(i,n,1){
if(i==n) rr[i]=n+1;
else{
if(a[i]!=a[i+1]) rr[i]=i+1;
else rr[i]=rr[i+1];
}
}
int tp=3e5;
rep(i,1,n){
int l=1,r=i,mid,ans=3e5;
while(r-l>1){
mid=l+r>>1;
if(p[i-1]-p[mid-1]>a[i]) l=mid;
else r=mid;
}
if(i-l>1&&l>ll[i-1]) l=ll[i-1];
if(l>0&&p[i-1]-p[l-1]>a[i]) ans=i-l;
else ans=tp;
l=i,r=n;
while(r-l>1){
mid=l+r>>1;
if(p[mid]-p[i]<=a[i]) l=mid;
else r=mid;
}
if(r-i>1&&r<rr[i+1]) r=rr[i+1];
if(r!=n+1&&p[r]-p[i]>a[i]) ans=min(ans,r-i);
else ans=min(tp,ans);
if(ans!=tp) cout<<ans<<" ";
else cout<<"-1 ";
}
cout<<endl;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】