VP记录:Codeforces Round 866 (Div. 2) A~C

传送门:CF

本来想写到D的,奈何D题是个比较烦的模拟题,所以就提前润了


A题:A. Yura’s New Name

简单的模拟题.发现 “^^” 我们是不需要考虑的,因为 “^” 要么被包含在"^“中,要么会被包含在” ^ _ ^ “中,所以只要考虑后者即可.
也就是对于每一个”_“,我们需要保证它的左右存在”^",实现的时候注意首尾即可

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define maxn 1000000
const double eps=1e-8;
#define	int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int main() {
	int T=read();
	while(T--) {
		string s;cin>>s;int ans=0;
		if(s.find('_')==s.npos) {
			if(s.length()==1) {
				cout<<1<<endl;
				continue;
			}
		}
		if(s[0]=='_') ans++;
		for(int i=1;i<s.length();i++) {
			if(s[i]=='_') {
				if(s[i-1]=='_') {
					ans++;
				}
			}
		}
		if(s.back()=='_') ans++;
		cout<<ans<<endl;
	}
	return 0;
}

B题:B. JoJo’s Incredible Adventures

刚开始想的是循环建图然后找最大子矩阵,但是想想比较难以实现,本题又是B题,应该没那么复杂,所以考虑找找规律.
首先如果全部都是1和0,我们可以直接特判出结果.
然后我们仔细观察一下样例:

0	1	1	1	1	0
0	0	1	1	1	1
1	0	0	1	1	1
1	1	0	0	1	1
1	1	1	0	0	1
1	1	1	1	0	0

我们会发现存在这样的一个结论,我们发现每一段连续的1在贡献最大矩阵的时候是相互独立.诶,这个怎么理解呢.想象一下,我们有两段1,其中被0隔开了,在我们的1往右循环的过程中,我们的这些0既隔开了左右两边又隔开了上下.然后我们的两段1就不可能在一起贡献出最大的子矩阵了.这个规律很重要,有了这个规律之后我们就可以独立考虑最长的那一段1即可(贪心的去想,显然最长的更容易产生更大的子矩阵!)

我们假设我们的连续的1的长度为n.不难发现:
n=1时,子矩阵大小为 1 ∗ 1 1*1 11
n=2时,子矩阵大小为 1 ∗ 2 1*2 12
n=3时,子矩阵大小为 2 ∗ 2 2*2 22
n=4时,子矩阵大小为 2 ∗ 3 2*3 23
等等,我们可以 O ( 1 ) O(1) O(1)解决这个问题.

但是存在一个细节需要考虑,因为我们的序列是循环的,也就是头尾相接,所以我们在找最长的连续的1时需要考虑环的情况,这个也很好解决,复制原本的链接在后边即可(老生常谈的解决方案了)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define maxn 1000000
const double eps=1e-8;
#define	int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int main() {
	int T=read();
	while(T--) {
		string s;cin>>s;int ans=0;
		if(s.find('_')==s.npos) {
			if(s.length()==1) {
				cout<<1<<endl;
				continue;
			}
		}
		if(s[0]=='_') ans++;
		for(int i=1;i<s.length();i++) {
			if(s[i]=='_') {
				if(s[i-1]=='_') {
					ans++;
				}
			}
		}
		if(s.back()=='_') ans++;
		cout<<ans<<endl;
	}
	return 0;
}

C题:Constructive Problem

我个人认为这道题是一道答辩题
首先题意很好理解,思路也很好想到,但是实现一言难尽
考虑如何使 M E X MEX MEX加一,我是分类来考虑的:

1.首先如果初始的 M E X MEX MEX(下面的 M E X MEX MEX全部都指初始情况,不再特意说明)是0,那么我们必然是可以解决的,只要将所有值赋值为0即可

2.如果我们序列中不存在 M E X + 1 MEX+1 MEX+1,注意这个是需要分开来考虑的,因为如果想要 M E X + 1 MEX+1 MEX+1,那么我们必然是将一个数改为 M E X MEX MEX,然后迫使它+1,但是如果我们本来存在 M E X + 1 MEX+1 MEX+1,此时我们可能变成了 M E X + 2 MEX+2 MEX+2.假设我们不存在 M E X + 1 MEX+1 MEX+1的话,我们有两种解决方案,要么我们数列中小于 M E X MEX MEX的数字个数不止一个,我们将这个数改为 M E X MEX MEX即可;要么我们可以将大于 M E X MEX MEX的数字拿出一个改为 M E X MEX MEX即可

3.我们的序列中存在 M E X + 1 MEX+1 MEX+1,我们必须要想办法删除所有的 M E X + 1 MEX+1 MEX+1,但是同时我们又需要出现一个 M E X MEX MEX,并且只能操作一次.所以我们只能找到包含所有的 M E X + 1 MEX+1 MEX+1的区间的最左端及最右端,记为 L , R L,R L,R,然后将这些数字全部改为 M E X MEX MEX(只有这样我们才能产生 M E X MEX MEX!).但是我们这个区间不一定只包含 M E X MEX MEX,可能在删除 M E X + 1 MEX+1 MEX+1的时候使其他数字又不存在了,所以我们又需要重新判断一下

博主本题的代码写的十分的丑陋,而且常数极大,在CF的C++14编译器上甚至被卡常,使用C++20才AC,所以只提供参考,具体实现方法在上述讲述中已经十分清楚了

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define maxn 1000000
const double eps=1e-8;
#define	int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int a[maxn];
map<int,int>mp,mp2;
int main() {
	int T=read();
	while(T--) {
		int n=read();int maxx=-int_INF;
		mp.clear();mp2.clear();
		for(int i=1;i<=n;i++) {
			a[i]=read();
			mp[a[i]]++;
		}
		int num=-1;
		for(int i=0;;i++) {
			if(mp[i]==0) {
				num=i;
				break;
			}
		}
		for(int i=1;i<=n;i++) {
			if(i==n) {
				mp2[a[i]]++;break;
			}
			if(a[i+1]==a[i]) {
				mp2[a[i]]++;
				while(a[i+1]==a[i]) i++;
			}
			else {
				mp2[a[i]]++;
			}
		}
		if(num==0) {
			puts("Yes");
			continue;
		}
		if(mp[num+1]) {
			vector<int>v;
			for(int i=1;i<=n;i++) {
				if(a[i]==num+1) {
					v.push_back(i);
				}
			}
			for(int i=0;i<v.size();i++) {
				if(i==v.size()-1) {
					mp[num+1]--;
				}
				else {
					for(int j=v[i];j<=v[i+1]-1;j++) {
						mp[a[j]]--;
					}
				}
			}
			int cnt=0;int kkk=0;
			for(int i=0;;i++) {
				if(mp[i]==0) {
					if(i==num+1) {
						kkk=1;
						break;
					}
					else {
						if(cnt==0) {
							cnt++;
						}
						else {
							kkk=0;
							break;
						}
					}
					
				}
			}
			if(kkk==1) {
				puts("Yes");
			}
			else {
				puts("No");
			}
			continue;
		}
		int flag=0;
		for(int i=0;i<num;i++) {
			if(mp[i]!=1) {
				flag=1;
				break;
			}
		}
		if(flag==1) {
			puts("Yes");
			continue;
		}
		for(int i=1;i<=n;i++) {
			if(a[i]>num) {
				flag=1;
				break;
			}
		}
		if(flag==1) {
			puts("Yes");
		}
		else {
			puts("No");
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值