Educational Codeforces 70 (Rated for Div.2) 题解

这篇博客详细解析了Educational Codeforces 70 (Rated for Div.2)比赛中的题目,包括A到D题。作者通过分析题意,给出了每道题目的思路和解决方案,涉及字符串处理、数学计算及算法应用。对于A题,重点在于理解如何找到满足条件的k,使二进制反序字典序最小;B题讨论了如何最少添加数码使得相邻数码差满足特定条件;C题通过前缀和和线段树解决机器人移动指令的矩形最小面积问题;D题则是一个构造1337字符串的题目。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么破烂场,题目根本不是Div.2难度的。算了,写个题解再说吧。

Ranklist:

在这里插入图片描述

A - You’re given two binary strings…

题意:有两个01字符串 a a a b b b,如果把每个字符串看作一个二进制数的话,都会对应一个十进制数。字符串 s s s对应的十进制数记作 f ( s ) f(s) f(s)。你要求出满足要求的非负整数 k k k,使得 f ( a ) + 2 k ∗ f ( b ) f(a)+2^k*f(b) f(a)+2kf(b)转化为二进制后反序的字典序尽可能小。题目包含多测。

思路:
刚上来就给你一个下马威,告诉你这不是普通的Div.2.
首先,我们知道,乘 2 2 2 k k k次方其实就是在二进制数后面添 k k k 0 0 0,而如果要让反序的字典序尽可能小,那么最后一位就要尽可能小,在满足这个的前提下,倒数第二位也要尽可能小,以此类推。
我们假设 b b b的后 x x x位全部都是 0 0 0,而从右往左数第 x x x位为 1 1 1。那么 k k k不论取什么值, f ( a ) + 2 k ∗ f ( b ) f(a)+2^k*f(b) f(a)+2kf(b)的后 x x x位永远不会发生改变,永远与 a a a的后 x x x位相同。此时,如果第 a a a的第 x x x位为 1 1 1,那么我们就不用添 0 0 0了,已经取到了最优解。而如果如果第 a a a的第 x x x位为 0 0 0,那么 f ( a ) + 2 k ∗ f ( b ) f(a)+2^k*f(b) f(a)+2kf(b)的第 x x x位为 1 1 1,不是我们想要的。此时我们就要在 b b b的后面添上一个 0 0 0,然后重复上述操作。
简单来说,假设 b b b最右边一个 1 1 1是从右往左数第 x x x位,那么本题要求的答案就是 a a a从右往左数第 x x x位左边有多少个连续的 0 0 0

C o d e : Code: Code:

#include <bits/stdc++.h>
using namespace std;
int T;
string s,t;
int main(){
	cin>>T;
	while(T--){
		cin>>s>>t;
		int ind=-1;
		for(int i=t.size()-1,cnt=0;i>=0;i--,cnt++){
			if(t[i]=='1'){
				ind=cnt;
				break;
			}
		}
		if(ind==-1){
			cout<<0<<endl;
			continue;
		}
		int ans=0;
		for(int i=s.size()-ind-1;i>=0;i--){
			if(s[i]=='0')	ans++;
			else			break;
		}
		cout<<ans<<endl;
	}
	return 0;
}

B - You’re given a decimal string…

题意:给你一个由 0 0 0 9 9 9组成的字符串。对于所有的 x = 0 , 1 , … , 9 x=0,1,\dots,9 x=0,1,,9 y = 0 , 1 , … , 9 y=0,1,\dots,9 y=0,1,,9,问你最少添加多少个数码使得相邻两个数码的差 m o d   10 mod\ 10 mod 10要么等于 i i i,要么等于 j j j,数码可以在任意位置添加。

思路:
思维难度没有上一题大,但是实现起来细节很恶心。
首先先 O ( 1 0 5 ) O(10^5) O(105)预处理出在 0 0 0 k k k之间最少添加多少个数码使得相邻两个数码的差 m o d   10 mod\ 10 mod 10要么等于 x x x,要么等于 y y y,用 a x , y , k a_{x,y,k} ax,y,k记录。然后暴力即可。但是有一个非常恶心的坑点:相邻两个字符相同的情况要特判。这也就是为什么现场很多人WA 2 2 2,从而没有过这道题的原因。我比赛的时候也在这里死了好几次。

C o d e : Code: Code:

#include <bits/stdc++.h>
using namespace std;
string s;
int a[11][11][11],ans[11][11];
signed main(){
	cin>>s;
	memset(ans,0,sizeof(ans));
	memset(a,63,sizeof(a));
	for(int i=0;i<10;i++)
		for(int j=0;j<10;j++)
			for(int k=0;k<10;k++){
				for(int p=0;p<=10;p++){
					for(int q=0;q<=10;q++){
						if((i*p+j*q)%10==k){
							int x=k;
							if(x==0)	x=10;
							if(x==10&&(p==0&&q==0))	continue;
							a[i][j][x]=min(a[i][j][x],p+q);
						}
					}
				}
			}
	for(int i=0;i<s.size()-1;i++){
		int dig1=s[i]-'0',dig2=s[i+1]-'0';
		int dif=(dig2-dig1+20)%10;
		if(dif==0)	dif=10;
		for(int j=0;j<10;j++){
			for(int k=0;k<10;k++){
				if(~ans[j][k]){
					if(a[j][k][dif]==0x3f3f3f3f)	ans[j][k]=-1;
					else							ans[j][k]+=a[j][k][dif]-1;
				}
			}
		}
	}
	for(int i=0;i<10;i++){
		for(int j=0;j<10;j++){
			cout<<ans[i][j]<<" ";
		}
		cout<<endl;
	}
	return 0;
}

C - You’re given a WASD string…

题意:有一个由“WASD"组成的字符串 s s s,代表机器人的移动命令。"W"表示向上移动一格,"A"表示向左移动一格,"S"表示向下移动一格,"D"表示向右移动一格。这个机器人的轨迹可以被一个矩形正好包围,现在允许你在 s s s的任意位置添上"WASD"中的一个字符,也可以不添,形成一个新的指令,使得这个矩形的面积尽可能小。题目包含多测。

思路:
我的做法可能和大家不太一样。
前缀和+线段树。首先我们先用前缀和将进行完第 i i i个指令之后机器人的位置求出来。我们记 s u m i , 0 sum_{i,0} sumi,0表示进行完第 i i i个指令之后机器人的横坐标, s u m i , 1 sum_{i,1} sumi,1表示进行完第 i i i个指令之后机器人的纵坐标。因此矩形的面积就是
S = ( m a x ( s u m [ 1 ] [ 0 ] , s u m [ 2 ] [ 0 ] , … , s u m [ n ] [ 0 ] , 0 ) − m i n ( s u m [ 1 ] [ 0 ] , s u m [ 2 ] [ 0 ] , … , s u m [ n ] [ 0 ] , 0 ) + 1 ) ∗ ( m a x ( s u m [ 1 ] [ 1 ] , s u m [ 2 ] [ 1 ] , … , s u m [ n ] [ 1 ] , 0 ) − m i n ( s u m [ 1 ] [ 1 ] , s u m [ 2 ] [ 1 ] , … , s u m [ n ] [ 1 ] , 0 ) + 1 ) ; S=(max(sum[1][0],sum[2][0],\dots,sum[n][0],0)-min(sum[1][0],sum[2][0],\dots,sum[n][0],0)+1)*(max(sum[1][1],sum[2][1],\dots,sum[n][1],0)-min(sum[1][1],sum[2][1],\dots,sum[n][1],0)+1); S=(max(sum[1][0],sum[2][0],,sum[n][0],0)min(sum[1][0],sum[2][0],,sum[n][0],0)+1)(max(sum[1][1],sum[2][1],,sum[n][1],0)min(sum[1][1],sum[2][1],,sum[n][1],0)+1);
而我们枚举每一个位置 i i i,在第 i i i个位置后面添上一个’W’其实就是 s u m i , 1 = s u m i − 1 , 1 + 1 , s u m i + 1 , 1 = s u m i , 1 + 1 , s u m i + 2 , 1 = s u m i + 1 , 1 + 1 , … s u m n + 1 , 1 = s u m n , 1 sum_{i,1}=sum_{i-1,1}+1,sum_{i+1,1}=sum_{i,1}+1,sum_{i+2,1}=sum_{i+1,1}+1,\dots sum_{n+1,1}=sum_{n,1} sumi,1=sumi1,1+1,sumi+1,1=sumi,1+1,sumi+2,1=sumi+1,1+1,sumn+1,1=sumn,1,其他也一样,这个可以用线段树来维护。

C o d e : Code: Code:

#include <bits/stdc++.h>
using namespace std;
#define int long long
int T;
string s;
int sum[200005][2];
struct seg{
	struct node{
		int l,r,mn,mx,lz;
	} s[800005];
	inline void build(int k,int l,int r,int type){
		s[k].l=l;s[k].r=r;
		if(l==r){
			s[k].mn=s[k].mx=sum[l][type];
			return;
		}
		int mid=(l+r)>>1;
		build(k<<1,l,mid,type);
		build(k<<1|1,mid+1,r,type);
		s[k].mn=min(s[k<<1].mn,s[k<<1|1].mn);
		s[k].mx=max(s[k<<1].mx,s[k<<1|1].mx);
	}
	inline void pushdown(int k){
		s[k<<1].mx+=s[k].lz;
		s[k<<1].mn+=s[k].lz;
		s[k<<1].lz+=s[k].lz;
		s[k<<1|1].mx+=s[k].lz;
		s[k<<1|1].mn+=s[k].lz;
		s[k<<1|1].lz+=s[k].lz;
	}
	inline void change(int k,int l,int r,int val){
		if(l>r)	return;
		if(l<=s[k].l&&s[k].r<=r){
			s[k].lz+=val;
			s[k].mx+=val;
			s[k].mn+=val;
			return;
		}
		pushdown(k);
		int mid=(s[k].l+s[k].r)>>1;
		if(r<=mid)		change(k<<1,l,r,val);
		else if(l>mid)	change(k<<1|1,l,r,val);
		else			change(k<<1,l,mid,val),change(k<<1|1,mid+1,r,val);
		s[k].mn=min(s[k<<1].mn,s[k<<1|1].mn);
		s[k].mx=max(s[k<<1].mx,s[k<<1|1].mx);
	}
	inline int query_mn(){
		return s[1].mn;
	}
	inline int query_mx(){
		return s[1].mx;
	}
} st[2];
int area(){
	return (max(st[0].query_mx(),0ll)-min(st[0].query_mn(),0ll)+1)*(max(st[1].query_mx(),0ll)-min(st[1].query_mn(),0ll)+1);
}
signed main(){
	cin>>T;
	while(T--){
		cin>>s;
		s=" "+s;
		for(int i=0;i<=s.size();i++)	sum[i][0]=sum[i][1]=sum[i][2]=sum[i][3]=0;
		for(int i=1;i<s.size();i++){
			sum[i][0]=sum[i-1][0];
			sum[i][1]=sum[i-1][1];
			if(s[i]=='W')	sum[i][0]++;
			if(s[i]=='A')	sum[i][1]--;
			if(s[i]=='S')	sum[i][0]--;
			if(s[i]=='D')	sum[i][1]++;
		}
		st[0].build(1,1,s.size()-1,0);
		st[1].build(1,1,s.size()-1,1);
		int ans=area();
		for(int i=0;i<=s.size();i++){
			st[0].change(1,i,s.size()-1,-1);
			ans=min(ans,(max(st[0].query_mx(),0ll)-min(st[0].query_mn(),min(sum[i-1][0]-1,0ll))+1)*(max(st[1].query_mx(),0ll)-min(st[1].query_mn(),0ll)+1));
			st[0].change(1,i,s.size()-1,1);
			st[0].change(1,i,s.size()-1,1);
			ans=min(ans,(max(st[0].query_mx(),max(sum[i-1][0]+1,0ll))-min(st[0].query_mn(),0ll)+1)*(max(st[1].query_mx(),0ll)-min(st[1].query_mn(),0ll)+1));
			st[0].change(1,i,s.size()-1,-1);
			st[1].change(1,i,s.size()-1,-1);
			ans=min(ans,(max(st[0].query_mx(),0ll)-min(st[0].query_mn(),0ll)+1)*(max(st[1].query_mx(),0ll)-min(st[1].query_mn(),min(sum[i-1][1]-1,0ll))+1));
			st[1].change(1,i,s.size()-1,1);
			st[1].change(1,i,s.size()-1,1);
			ans=min(ans,(max(st[0].query_mx(),0ll)-min(st[0].query_mn(),0ll)+1)*(max(st[1].query_mx(),max(sum[i-1][1]+1,0ll))-min(st[1].query_mn(),0ll)+1));
			st[1].change(1,i,s.size()-1,-1);
		}
		cout<<ans<<endl;
	}
	return 0;
}

D - Print a 1337-string…

题意:给定一个正整数 n n n,让你构造出一个由"1",“3”,“7"组成的字符串,其中恰好含有 n n n个字串"1337”。题目包含多测。

思路:
这道题可谓是有些毒瘤了,分明是水题,还不放在A或者B题,偏偏跑到个D的位置来,害得我现场做完C没时间做D。
很容易想到 133...33733...33733...337 133...33733...33733...337 133...33733...33733...337的情况,然后随便乱搞搞就行了,水题不多说。

C o d e : Code: Code:

#include <bits/stdc++.h>
using namespace std;
#define int long long
vector<int> ans;
signed main(){
	int T;cin>>T;
	while(T--){
		int n;cin>>n;
		ans.clear();
		putchar('1');
		for(int i=50005;i>=1;i--){
			while(n>=(i*(i+1)/2)){
				ans.push_back(i+1);
				n-=i*(i+1)/2;
			}
		}
		sort(ans.begin(),ans.end());
		for(int i=1;i<=ans[0];i++)	putchar('3');putchar('7');
		for(int i=1;i<ans.size();i++){
			for(int j=1;j<=ans[i]-ans[i-1];j++)
				putchar('3');
			putchar('7');
		}
		cout<<endl;
	}
}

有巨佬会E或者F的在洛谷上发私信给我,我的洛谷账号是这个,或者在下面评论哦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值