文章目录
一、概况
比赛名称: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) (ai−1+ai−2)∤(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
1∼n 中不重复的恰好选出
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],li≤ri;
- ∀ i ∈ [ 2 , k ] , l i = r i − 1 + 1 ; \forall i\in [2,k],l_i=r_{i-1}+1; ∀i∈[2,k],li=ri−1+1;
给定 q q q 次操作,每次给定一个正整数 x ( 1 ≤ x ≤ n ) x(1\le x\le n) x(1≤x≤n)。一次操作如下:
- 找到值 i i i 使得 l i ≤ x ≤ r i l_i\le x\le r_i li≤x≤ri(显然这样的 i i i 是唯一的)。
- 令 a = min ( x , r i + l i − x ) a=\min(x,r_i+l_i-x) a=min(x,ri+li−x),令 b = max ( x , r i + l i − x ) b=\max(x,r_i+l_i-x) b=max(x,ri+li−x)。
- 翻转 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,…,sa−1,sb,sb−1,…,sa+1,sa,sb+1,sb+2,…,sn−1,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)(1≤l≤r≤n) 表示 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&…&ar−1&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+2j−1] 的与运算值。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;
}
三、总结
如果是前面的题,不要想的很复杂,很可能只是个数学题或思维题,找找规律。
如果做到后面,就要往数据结构方面想,能用什么用什么 。