AtCoder Beginner Contest 265 D Iroha and Haiku (New ABC Edition)

\(O{(n\log n)}\) 做法

我在考场上只想到此做法,不难想到,可以将三段用二分预处理。
\(xs[i]\)表示从\(a_i\)开始总和为\(P\)的末尾编号,可以用二分处理。
最后 \(O(n)\) 判断即可。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=2e5+5;
const ll INF=0x3f3f3f3f;
ll n,x,y,z;
ll a,s[N];
ll xs[N],ys[N],zs[N];
int main(){
	scanf("%lld%lld%lld%lld",&n,&x,&y,&z);
	for(ll i=1;i<=n;i++){
		scanf("%lld",&a);
		s[i]=s[i-1]+a;
	}
	for(ll i=1;i<=n;i++){
		ll l=i,r=n;
		while(l<=r){
			ll mid=(l+r)/2;
			ll num=s[mid]-s[i-1];
			if(num>x) r=mid-1;
			else if(num<x) l=mid+1;
			else{
				l=mid;
				break;
			}
		}
		ll num=s[l]-s[i-1];
		if(num==x) xs[i]=l;
		else xs[i]=INF;
	}
 
	for(ll i=1;i<=n;i++){
		ll l=i,r=n;
		while(l<=r){
			ll mid=(l+r)/2;
			ll num=s[mid]-s[i-1];
			if(num>y) r=mid-1;
			else if(num<y) l=mid+1;
			else{
				l=mid;
				break;
			}
		}
		ll num=s[l]-s[i-1];
		if(num==y) ys[i]=l;
		else ys[i]=INF;
	}
 
	for(ll i=1;i<=n;i++){
		ll l=i,r=n;
		while(l<=r){
			ll mid=(l+r)/2;
			ll num=s[mid]-s[i-1];
			if(num>z) r=mid-1;
			else if(num<z) l=mid+1;
			else{
				l=mid;
				break;
			}
		}
		ll num=s[l]-s[i-1];
		if(num==z) zs[i]=l;
		else zs[i]=INF;
	}
 
	for(ll i=1;i<=n;i++){
		if(xs[i]>=n) continue;
		if(xs[i]==INF) continue;
		if(ys[xs[i]+1]>=n) continue;
		if(ys[xs[i]+1]==INF) continue;
		if(zs[ys[xs[i]+1]+1]==INF) continue;
		printf("Yes");
		return 0;
	}
	printf("No");
	return 0;
}

\(O{(n)}\) 做法

不难发现,以上做法是有点浪费的,每个节点需分别计算,其实可以线性\(O(n)\)一次计算出\(n\)个节点。
添加一个指针,如果区间里的值小于目标值,则指针往右移。
因为指针最多移\(n\)次,所以这个算法是线性的。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=2e5+5;
const ll INF=0x3f3f3f3f;
ll n,x,y,z,xp,yp,zp;
ll a,s[N];
ll xs[N],ys[N],zs[N];
int main(){
	scanf("%lld%lld%lld%lld",&n,&x,&y,&z);
	for(ll i=1;i<=n;i++){
		scanf("%lld",&a);
		s[i]=s[i-1]+a;
	}
	for(ll i=1;i<=n;i++){
	    while(xp<n&&s[xp]-s[i-1]<x) xp++;
		if(s[xp]-s[i-1]==x) xs[i]=xp;
		else xs[i]=INF;
	}
 
	for(ll i=1;i<=n;i++){
	    while(yp<n&&s[yp]-s[i-1]<y) yp++;
		if(s[yp]-s[i-1]==y) ys[i]=yp;
		else ys[i]=INF;
	}
	
	for(ll i=1;i<=n;i++){
	    while(zp<n&&s[zp]-s[i-1]<z) zp++;
		if(s[zp]-s[i-1]==z) zs[i]=zp;
		else zs[i]=INF;
	}
 
	for(ll i=1;i<=n;i++){
		if(xs[i]>=n) continue;
		if(xs[i]==INF) continue;
		if(ys[xs[i]+1]>=n) continue;
		if(ys[xs[i]+1]==INF) continue;
		if(zs[ys[xs[i]+1]+1]==INF) continue;
		printf("Yes");
		return 0;
	}
	printf("No");
	return 0;
}
posted @ 2022-08-23 14:12  KevinLikesCoding  阅读(140)  评论(0)    收藏  举报