Codeforces Round 900 (Div. 3) 补题报告(A~E)

比赛链接

一、概况

比赛名称:Codeforces Round 900 (Div. 3)
日期:2023年11月18日 星期六

忙于期中考试,没打。
A、B很简单,是数学题;C有思维,能想出来就好写了。
D、E有难度。

二、正解

A. How Much Does Daytona Cost?

1 题目

判断数组 a a a 中是否存在一个非空子段使得 k k k 在这个子段中出现的次数严格大于其它数字的出现次数。存在输出YES,否则输出NO

2 思路

当k存在于该数组中时,选取子段使得仅包含k即可。此时出现次数占100%。

3 代码

#include<bits/stdc++.h>
#define ll long long
#define bug printf("---OK---")
#define pa printf("A: ")
#define pr printf("\n")
#define pi acos(-1.0)
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
ll q;
void solve(){
	ll n,k;
	bool flag=0;
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		ll x;
		cin>>x;
		if(x==k){
			flag=1;
		}
	}
	if(flag==1){
		cout<<"YES";
	}
	else{
		cout<<"NO";
	}
}
int main(){
	cin>>q;
	while(q--){
		solve();pr;
	}
    return 0;
}

B. Aleksa and Stack

1 题目

t t t组数据。每组数据给定 n n n。请你构造一个长度为 n n n的数组,使得该数组满足 ( a i − 1 + a i − 2 ) ∤ ( 3 × a i ) (a_{i-1}+a_{i-2})\nmid(3\times a_i) (ai1+ai2)(3×ai)

2 思路

考虑若每个数字都为奇数,由于两个奇数相加必为偶数,3 × \times ×一个奇数必为奇数,因此一定满足条件。

3 代码

#include<bits/stdc++.h>
#define ll long long
#define bug printf("---OK---")
#define pa printf("A: ")
#define pr printf("\n")
#define pi acos(-1.0)
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
ll q;
void solve(){
	ll n;
	cin>>n;
	for(int i=1;i<=n;i++){
		cout<<i*2-1<<" ";
	}
}
int main(){
	cin>>q;
	while(q--){
		solve();pr;
	}
    return 0;
}

C. Vasilije in Cacak

1 题目

给定 n , k , x n,k,x n,k,x ,判断能否在 1 ∼ n 1\sim n 1n不重复的恰好选出 k k k 个数使得这 k k k 的数的和为 x x x
可以选出输出 YES,否则输出 NO

2 思路

将前 k k k 个数字相加,为最小的可能的 x x x ;后 k k k 个数字相加,为最大的可能的 x x x 。由于公差为 1 1 1 ,因此在此区间内所有的数均可满足条件。

3 代码

#include<bits/stdc++.h>
#define ll long long
#define bug printf("---OK---")
#define pa printf("A: ")
#define pr printf("\n")
#define pi acos(-1.0)
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
int solve(){
	ll n,k,x;
	cin>>n>>k>>x;
	ll minn,maxx;
	//用等差数列求和公式降低时间复杂度
	minn=(1+k)*k/2;//1为首项,k为末项,项数是k(因为公差是1)
	maxx=(n-k+1+n)*k/2;//n-k+1为首项,n为末项,项数是k
	if(x>=minn&&x<=maxx){
		cout<<"YES";
	}
	else{
		cout<<"NO";
	}
}
int main(){
	int t;
	cin>>t;
	while(t--){
		solve();pr;
	}
	return 0;
}

D. Reverse Madness

1 题目

给定一个长度为 n n n 的仅包含小写拉丁字母的字符串 s s s ,以及两个长度为 k k k 的正整数序列 l , r l,r l,r,保证这两个序列满足:

  • l 1 = 1 ; l_1=1; l1=1;
  • r k = n r_k=n rk=n;
  • ∀ i ∈ [ 1 , k ] , l i ≤ r i ; \forall i\in [1,k],l_i\le r_i; i[1,k],liri;
  • ∀ i ∈ [ 2 , k ] , l i = r i − 1 + 1 ; \forall i\in [2,k],l_i=r_{i-1}+1; i[2,k],li=ri1+1;

给定 q q q 次操作,每次给定一个正整数 x ( 1 ≤ x ≤ n ) x(1\le x\le n) x(1xn)。一次操作如下:

  • 找到值 i i i 使得 l i ≤ x ≤ r i l_i\le x\le r_i lixri(显然这样的 i i i 是唯一的)。
  • a = min ⁡ ( x , r i + l i − x ) a=\min(x,r_i+l_i-x) a=min(x,ri+lix),令 b = max ⁡ ( x , r i + l i − x ) b=\max(x,r_i+l_i-x) b=max(x,ri+lix)
  • 翻转 s [ a , b ] s_{[a,b]} s[a,b]

其中“翻转 s [ a , b ] s_{[a,b]} s[a,b]”的意思是让 s s s 变为
s 1 , s 2 , … , s a − 1 , s b , s b − 1 , … , s a + 1 , s a , s b + 1 , s b + 2 , … , s n − 1 , s n s_1,s_2,\ldots ,s_{a-1},s_b,s_{b-1},\ldots ,s_{a+1},s_a,s_{b+1},s_{b+2},\ldots ,s_{n-1},s_n s1,s2,,sa1,sb,sb1,,sa+1,sa,sb+1,sb+2,,sn1,sn

最后一次操作后,输出 s s s

2 思路

虽然平衡树简单无脑,但本人没学过……
考虑一种巧妙的做法:
发现字符串反转时两两对应,因此只有该字符串翻转奇数次时,才真正被翻转。而每次翻转时打标记太慢,因此考虑在每次输入 x x x 时,根据 x x x 就可以计算出需要翻转的区间。
为了方便,在输入r数组时采用映射(map)

map<int,int> mp;
for(int i=1;i<=k;i++){
	cin>>r[i];
	for(int j=l[i];j<=r[i];j++){
		mp[j]=i;
	}
}

所以 x x x 属于 m p [ x ] mp[x] mp[x] 区间内。将开头和末尾打标记,最后模拟时传递即可。

d[min(x,l[mp[x]]+r[mp[x]]-x)]++  //左端点 ++
d[max(x,l[mp[x]]+r[mp[x]]-x)+1]--;//右端点+1 --

3 代码

#include<bits/stdc++.h>
#define ll long long
#define bug printf("---OK---")
#define pa printf("A: ")
#define pr printf("\n")
#define pi acos(-1.0)
#define TIE ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)//懒得用printf和scanf~~~
using namespace std;
const ll N=2e5+5;
int l[N],r[N];
map<int,int> mp;
int d[N];
void solve(){
	memset(l,0,sizeof l);
	memset(r,0,sizeof r);
	memset(d,0,sizeof d);
	mp.clear();
	int n,k,q,x;
	string s;
	cin>>n>>k;
	cin>>s;
	for(int i=1;i<=k;i++){
		cin>>l[i];
	}
	for(int i=1;i<=k;i++){
		cin>>r[i];
		for(int j=l[i];j<=r[i];j++){
			mp[j]=i;
		}
	}
	cin>>q;
	while(q--){
		cin>>x;
		d[min(x,l[mp[x]]+r[mp[x]]-x)]++;
		d[max(x,l[mp[x]]+r[mp[x]]-x)+1]--;
	}
	for(int i=1;i<=n;i++){
		d[i]+=d[i-1];
	}
	for(int i=1;i<=k;i++){
		for(int j=l[i];j<=l[i]+r[i]>>1;j++){
			if(d[j]%2==1){
				swap(s[j-1],s[l[i]+r[i]-j-1]);
			}
		}
	}
	cout<<s<<endl;
}
int main(){
	TIE;
	int t;
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

E. Iva & Pav

1 题目

t t t 组数据。每组数据给定长度为 n n n 的数组 a a a q q q 次询问。

我们定义 f ⁡ ( l , r ) ( 1 ≤ l ≤ r ≤ n ) \operatorname{f}(l,r)(1\le l\le r\le n) f(l,r)(1lrn) 表示 a l & a l + 1 & … & a r − 1 & a r a_l\And a_{l+1}\& \dots\& a_{r-1}\&a_r al&al+1&&ar1&ar 的结果。其中, & \& & 表示位与运算。

对于每次询问,将给定 l , k l,k l,k。请你找到最大的 r r r 使得 f ⁡ ( l , r ) ≥ k \operatorname{f}(l,r)\ge k f(l,r)k。如果无解,输出 -1

2 思路

在与运算中,多个重复数字参与运算时,对结果没有影响,且区间不会改变,因此属于可重复贡献问题,考虑使用ST表。
大体做法如下:

  • 1: 预处理ST表,f[i][j]表示 [ i , i + 2 j − 1 ] [i,i+2^j-1] [i,i+2j1] 的与运算值。
  • 2:进行询问,二分 r r r
  • 3:判断是否符合要求。

3 代码

#include<bits/stdc++.h>
#define ll long long
#define bug printf("---OK---")
#define pa printf("A: ")
#define pr printf("\n")
#define pi acos(-1.0)
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
const int N=2e5+10;
int f[N+2][40];
int ans[N+2],cnt;
void solve(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&f[i][0]);
	}
	for(int j=1;j<=log2(n);j++){
		for(int i=1;i+(1<<j)-1<=n;i++){
			f[i][j]=f[i][j-1]&f[i+(1<<(j-1))][j-1];
		}
	}
	int q;
	scanf("%d",&q);
	while(q--){
		int l,k;
		scanf("%d%d",&l,&k);
		if(f[l][0]<k){
			ans[++cnt]=-1;
		}
		else{
			int L=l,R=n;
			while(L<R){
				int mid=(L+R+1)/2;
				int s=log2(mid-l+1);
				int tmp=f[l][s]&f[mid-(1<<s)+1][s];
				if(tmp>=k){
					L=mid;
				}
				else{
					R=mid-1;
				}
			}
			ans[++cnt]=L;
		}
	}
}
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		solve();pr;
	}
	for(int i=1;i<=cnt;i++){
		printf("%d ",ans[i]);
	}
	return 0;
}

三、总结

如果是前面的题,不要想的很复杂,很可能只是个数学题或思维题,找找规律。
如果做到后面,就要往数据结构方面想,能用什么用什么 。

  • 54
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值