Codeforces Round 1075 (Div. 2) 题解

一、前言

自从 上次 CF 怒涨百分 之后,我的大号(Timmyliuyunxi)终于又一次迎来了大爆发,怒涨两百余分,这可能是我之前和一年之后的最好的一次表现了,甚至封存在了 CF vlog 中,真是倍感荣幸。

我发现自己的表现异常不稳定, rating 如同波浪般跌宕起伏,涨分全靠 Rp,掉分积攒 Rp。至于这次的 Rp,可能是模拟赛爆零,期末考试没有考好,亦或是因迟到获得处分......

咳咳,我们言归正传。比赛链接

二、题解

第 A 题 Table with Numbers

本题题目有些细节,我差点因为读错题而耽误时间。

题意为给定一些数 n u m 1 , n u m 2 , ... , n u m x num_1,num_2,\dots,num_x num1,num2,...,numx和 n , m n,m n,m,问最多能组成多少个数对 ( a , b ) (a,b) (a,b),使得 a ≤ n a\le n a≤n 且 b ≤ m b\le m b≤m。

考虑贪心,我们不妨设 n ≤ m n\le m n≤m(否则交换数对顺序),则我们的 ( a , b ) (a,b) (a,b) 也满足 a ≤ b a\le b a≤b。

此时我们的限制就是 a , b ≤ m a,b\le m a,b≤m 和 a ≤ n a\le n a≤n。

前者限制条件为 a n s ≤ ∑ [ n u m x ≤ m ] 2 ans\le \frac{\sum [num_x\le m]}{2} ans≤2∑[numx≤m];后者限制条件为 a n s ≤ ∑ [ n u m x ≤ n ] ans\le\sum[num_x\le n] ans≤∑[numx≤n]。

取最小值输出即可。

代码:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int t; cin>>t;
	while (t--){
		int n,x,y,a=0,b=0; cin>>n>>x>>y;
		for (int i=1; i<=n; i++){
			int val; cin>>val;
			if (val<=min(x,y)) a++,b++;
			else if (val<=max(x,y)) a++;
		}
		cout<<min(a/2,b)<<"\n";
	}
}

第 B 题 The Curse of the Frog

题意为给你一只青蛙,它有 n n n 种移动方法,第 i i i 种是向前跳不超过 a i a_i ai 步,但每 b i b_i bi 次会被击退 c i c_i ci 步,问至少击退几次可以走超过 m m m 步。

仍旧贪心。我们先把每种移动方式移动 b i − 1 b_i-1 bi−1 次,此时没有被击退。

接下来青蛙可以用一次击退的代价来前进 a i × b i − c i a_i\times b_i-c_i ai×bi−ci 步,贪心选择最大的走即可。 注意一下无解是所有的前进步数都为负且初始并未到达目标。

代码:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int t; cin>>t;
	while (t--){
		int n,m,a=0,b=-1000000000; cin>>n>>m;
		for (int i=1; i<=n; i++){
			int x,y,z; cin>>x>>y>>z;
			a+=x*(y-1); b=max(b,x*y-z);
		}
		if (a>=m) cout<<"0\n";
		else if (b<=0) cout<<"-1\n";
		else cout<<(m-a+b-1)/b<<"\n";
	}
}

第 C1 题 XOR Convenience (Easy Version)

小清新构造。题意简单,这里就不说了。

我们观察题目的限制,若 a n = 1 a_n=1 an=1 则更易成功。

我们尝试构造满足 a i ⊕ i = 1 a_i \oplus i=1 ai⊕i=1 的 a a a(首、尾)除外。

易得 a i = i ⊕ 1 a_i =i\oplus 1 ai=i⊕1 ,我们发现这个 i ⊕ 1 i\oplus 1 i⊕1 在 2 ≤ i < n 2\le i<n 2≤i<n 时,它的值在 [ 2 , n ] [2,n] [2,n] 之间且不会重复。直接构造或分奇偶性构造即可。

代码:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int t; cin>>t;
	while (t--){
		int n; cin>>n;
		if (n==1){
			cout<<1<<"\n";
			continue;
		} 
		if (n%2==0){
			cout<<n<<" ";
			for (int i=2; i<n; i+=2){
				cout<<i+1<<" "<<i<<" ";
			}
			cout<<1<<"\n";
		}
		else{
			cout<<n-1<<" ";
			for (int i=2; i<n-1; i+=2){
				cout<<i+1<<" "<<i<<" ";
			}
			cout<<n<<" 1\n";
		}
	}
}

第 C2 题 XOR Convenience (Hard Version)

小清新构造。题意简单,这里就不说了。

考虑 C1 的想法,但是这里出现了第一个数的限制,也就是说第一个数我们也要满足条件。 观察第一个数,由于 a 1 ⊕ 1 ≠ a 1 ≠ 0 a_1\oplus 1\neq a_1\neq 0 a1⊕1=a1=0,所以我们只需要保证 a 1 ⊕ 1 ≠ n + 1 a_1\oplus 1\neq n+1 a1⊕1=n+1 即可。

我们首先构造出一组 C1 的解,设为 a a a。

那么若第一个数不满足条件,则交换 a 1 a_1 a1 和 a x a_x ax 使得 a x = a 1 & ( a 1 − 1 ) a_x=a_1 \& (a_1-1) ax=a1&(a1−1)。

那么我们这次操作会让 1 1 1 脱离危险, a x ⊕ x a_x\oplus x ax⊕x 增加 a 1 − a 1 & ( a 1 − 1 ) a_1-a_1\&(a_1-1) a1−a1&(a1−1),易得其大于 x x x,那么容易发现合法。

特殊的,当 n = 2 i n=2^i n=2i 时,无解,因为若其放置在最后一个位置,那么倒数第二个位置非法,否则这个位置非法。

代码:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int t; cin>>t;
	while (t--){
		int n; cin>>n;
		if (n==1){
			cout<<1<<"\n";
			continue;
		} 
		if (n%2==0){
			int val=(n&(n-1));
			if (val==0) cout<<"-1\n";
			else{
				int x=min(val,n-val);
				cout<<x<<" ";
				for (int i=2; i<n; i+=2){
					cout<<(i+1==x?n:i+1)<<" "<<(i==x?n:i)<<" ";
				}
				cout<<1<<"\n";
			}
		}
		else{
			cout<<n-1<<" ";
			for (int i=2; i<n-1; i+=2){
				cout<<i+1<<" "<<i<<" ";
			}
			cout<<n<<" 1\n";
		}
	}
}

第 D1 题 Little String (Easy Version)

小清新计数,题意简单,就不多说了。

考虑动态插入点。那么我们发现若 s i = 0 s_i=0 si=0,则 i + 1 i+1 i+1 处于 1 1 1 到 n n n 最左侧的最右侧之间。否则不在它们之间。

若 s i = 0 s_i=0 si=0,则答案乘 i i i,否则乘 2 2 2。 特殊的 s n s_n sn 一定为 1 1 1,否则不合法。

模拟即可,注意答案不是模 c c c 而是 998244353 998244353 998244353。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define M 1000000007
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int t; cin>>t;
	while (t--){
		int n,m; cin>>n>>m;
		int ans=1,ans2=1;
		string s; cin>>s;
		if (s[0]!='1'||s[n-1]!='1'){
			cout<<"-1\n";
			continue;
		}
		for (int i=0; i<n-1; i++){
			if (s[i]=='1') ans=ans*2%m,ans2=ans2*2%M;
			else ans=ans*i%m,ans2=ans2*i%M;
		}
		ans%=m;
		if (ans==0) cout<<"-1\n";
		else cout<<ans2<<"\n";
	}
}

第 D2 题 Little String (Hard Version)

考虑 D1,那么我们的任务是补全 ? 使得其不是 c c c 的倍数。

设 c = 2 a × b c=2^a\times b c=2a×b,我们已经填的乘积为 x x x,还有 y y y 个数。则我们分一些情况讨论。若 x × 2 y m o d    c ≠ 0 x\times2^y \mod c\neq 0 x×2ymodc=0,则我们都填 2 2 2。

否则我们先尽可能填 2 2 2,填不动后尝试填小的奇数,容易发现这是正确的。

代码:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define M 1000000007
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int t; cin>>t;
	while (t--){
		int n,m; cin>>n>>m;
		int ans=1,ans2=1;
		string s; cin>>s;
		if (s[0]=='0'||s[n-1]=='0'){
			cout<<"-1\n";
			continue;
		}
		if (s[0]=='?') s[0]='1';
		for (int i=0; i<n-1; i++){
			if (s[i]=='1') ans=ans*2%m,ans2=ans2*2%M;
			else if (s[i]=='0') ans=ans*i%m,ans2=ans2*i%M;
		}
		for (int i=0; i<n-1; i+=2){
			if (s[i]=='?') ans=ans*2%m,ans2=ans2*2%M;
		}
		ans%=m;
		if (ans==0) cout<<"-1\n";
		else{
			for (int i=(n-2)-(n%2==0); i>1; i-=2){
				if (s[i]!='?') continue;
				if (ans*2%m!=0) ans=ans*2%m,ans2=ans2*2%M;
				else ans=ans*i%m,ans2=ans2*i%M;
			}
			cout<<ans2<<"\n";
		}
	}
}

第 E 题 Majority Wins?

大粪套,但是题目题意较为简单。

考虑多种情况。

Case 0 ∣ s ∣ = 1 |s|=1 ∣s∣=1

参见样例。

Case 1 无解

没有 1 1 1,否则我们可以将左右两边缩成 0 0 0,然后左右分别将 01 01 01 变为 1 1 1 即可,这时代价为 n + 1 n+1 n+1。

Case 2 答案为 n n n

只能操作整个串,只需要满足整个字符串中 1 1 1 的个数不少于 0 0 0 的个数。

Case 3 答案为 n + 1 n+1 n+1

只可能操作一个前后缀,然后操作整个串,前缀和即可。

Case 4 答案为 n + 2 n+2 n+2

如果某一个前后缀能变成 1 1 1,那么剩下的一段变为 0 0 0,合并即可。

还有一种情况 (这是我唯一一次罚时的地方)就是两个 1 1 1,左边合一下,右边合一下,最后将这个 0110 0110 0110 合并即可。

Case 5 剩余情况

根据 Case1 的构造,这时答案为 n + 3 n+3 n+3。

代码:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
int pre[5000010],suf[5000010];
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int t; cin>>t;
	while (t--){
		int n; cin>>n;
		string s; cin>>s; s=" "+s;
		for (int i=1; i<=n; i++){
			pre[i]=pre[i-1]+(s[i]=='1'?1:-1);
		}
		suf[n+1]=0;
		for (int i=n; i>=1; i--){
			suf[i]=suf[i+1]+(s[i]=='1'?1:-1);
		}
		if (n==1&&pre[n]==1) cout<<"0\n";
		else if (pre[n]==-n) cout<<"-1\n";
		else if (pre[n]>=0) cout<<n<<"\n";
		else{
			bool ok0=0;
			for (int i=1; i<n; i++){
				if ((pre[i]<0?-1:1)+suf[i+1]>=0) ok0=1;
			}
			for (int i=2; i<=n; i++){
				if ((suf[i]<0?-1:1)+pre[i-1]>=0) ok0=1;
			}
			bool ok1=0;
			for (int i=1; i<=n; i++){
				if (pre[i]>=0||suf[i]>=0||(s[i]=='1'&&s[i-1]=='1')) ok1=1;
			}
			if (ok0==1) cout<<n+1<<"\n";
			else if (ok1) cout<<n+2<<"\n";
			else cout<<n+3<<"\n";
		}
	}
}

三、后记

题目以贪心、DP、思维为主,都是我擅长的,下次考个别的我就不会了。

顺便推荐一下 LYXOI(洛谷 92018)我们会举办 SFFC(春节解密赛),欢迎各位抽出宝贵的时间参加。 LYXOI 链接

相关推荐
hadage2332 小时前
--- 力扣oj柱状图中最大的矩形 单调栈 ---
算法·leetcode·职场和发展
json{shen:"jing"}2 小时前
18. 四数之和
数据结构·算法·leetcode
千逐-沐风2 小时前
SMU-ACM2026冬训周报1st
算法
天赐学c语言2 小时前
1.25 - 零钱兑换 && 理解右值以及move的作用
c++·算法·leecode
北冥湖畔的燕雀2 小时前
C++智能指针:告别内存泄漏的利器
c++·算法
傻乐u兔2 小时前
C语言进阶————数据在内存中的存储1
c语言·数据结构·算法
多米Domi0112 小时前
0x3f 第42天 复习 10:39-11:33
算法·leetcode
thubier(段新建)2 小时前
单招模考试卷模型思考(1)
算法·单招
议题一玩到2 小时前
#leetcode# 1984. Minimum Difference Between Highest and Lowest of K Scores
数据结构·算法·leetcode