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]内:

  1. 和相同
  2. a[i]!=b[i]
  3. 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;
}

posted @ 2024-02-24 01:51  mono_4  阅读(27)  评论(0编辑  收藏  举报