Codeforces Round #637 (Div. 2) - Thanks, Ivan Belonogov!(B.尺缩法/前缀和,C.模拟,D【待解】)

B. Nastya and Door

题目大意:

  有个长度为n的序列,如果ai>ai-1且ai>ai+1,那么ai即为顶峰,问在给定的区间长度k,求包含最多顶峰的区间数+1,若答案有多个,则取左端点最小,注意:端点不算顶峰

解题思路:

第一种方法:前缀和
  把每个顶点标记出来,然后利用前缀和统计前i区间有多少个顶峰,然后就判断sum[l+k-1]-sum[l-1]有多少个顶峰,求最大即可,注意:要把边界上的顶峰删除
第二种方法:尺缩法
  首先将左端点l=1,右端点r=l+k-1,然后先暴力,算出这个区间的顶峰
  因为区间固定,每次l++,r++,然后判断新成为边界的l是否是上一个区间的顶峰,是就顶峰数减一,且要判断上一个区间的右边界是否为顶峰,是就顶峰数加一

AC代码:

#include
using namespace std;
int T,k,n,ans,lans;
int a[200100],sum[200100];
int main() {
	scanf("%d",&T);
	while(T--) {
		ans=0,lans=1;
		scanf("%d%d",&n,&k);
		for(int i=1; i<=n; i++) scanf("%d",&a[i]);
		int l=1,r=l+k-1,tmp=0;
		for(int i=l; i<=r; i++) //先暴力统计第一个区间的顶峰数
		    if(i!=l&&i!=r&&a[i]>a[i-1]&&a[i]>a[i+1]) tmp++;
		if(tmp>ans) ans=tmp,lans=l;
		while(r<=n-1) {
			l++,r++;
			if(a[l]>a[l-1]&&a[l]>a[l+1]) tmp--;//判断成为边界的左端点是否是顶峰
			if(a[r-1]>a[r-2]&&a[r-1]>a[r]) tmp++;//判断上一个区间的右端点是否是顶峰
			if(tmp>ans) ans=tmp,lans=l;
		}
		printf("%d %d\n",ans+1,lans);//别忘了加一
	}
}

C. Nastya and Strange Generator

题目大意:

  一大长串英文实在是看累了,其实题目就是让你判断一个序列是否能够满足如下规律:
  例如:6 7 4 5 1 2 3
  1能够到最后这个序列的最后一位都是连续的,然后把这段删除
  删除后序列:6 7 4 5
  4能够到最后这个序列的最后一位都是连续的,然后把这段删除
  删除后序列:6 7
  6能够到最后这个序列的最后一位都是连续的,然后把这段删除
  删除后序列:无
  如果能够一直按照以上规律到最后删除序列为空为止,就输出YES,否则输出NO

解题思路:

  没啥好说的,把删除这一麻烦的操作,改成标记即可

AC代码:

#include
using namespace std;
int T,n;
int p[100100],pos[100100],vis[100100];
int main() {
	scanf("%d",&T);
	while(T--) {
		scanf("%d",&n);
		for(int i=1; i<=n; i++) {
			scanf("%d",&p[i]);
			pos[p[i]]=i;//pos[]记录每个数在p[]中的位置
		}
		int num=1,f=1,loc=pos[num];//从1开始,f代表这个序列是否可行
		vis[loc]=vis[n+1]=1;//初始化为1
		while(num<=n&&f) {
		//	cout<<"loc="<++;//位置加加
			if(vis[loc]) loc=pos[++num],vis[loc]=1;//如果当前位置被访问过
			//意味着被删除(vis[n+1]=1也是这个道理)
			else {//如果未被访问过
				if(p[loc]==num+1) num++,vis[loc]=1;//如果是符合连续的,则继续操作
				else f=0;//如果不连续,则把标记置为0
			}
		}
		if(f) printf("YES\n");
		else printf("NO\n");
		for(int i=1; i<=n+1; i++) vis[i]=pos[i]=p[i]=0;//重新赋值,因为有多组数据
	}
} 

你可能感兴趣的:(codeforces)