A1&A2 Gardener and the Capybaras
题意
简单版本: A1
困难版本: A2
一个字符串,只包含a和b两种字母,请将这个字符串划分为三个非空字符串,使得第二个字符串的字典序最大或最小。困难版本的数据范围要求此题在线性时间复杂度下解决。
思路
就是用分类讨论的思想去构造,具体的构造方法见代码。这种构造题的构造方法很多,顺水推舟就能做出来。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 5e5 + 5;
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
ll t;
cin>>t;
while(t--){
string s;
cin>>s;
ll n=s.length()-1;
if(s[0]=='a'&&s[n]=='a'){
cout<<"a"<<" ";
for(ll i=1;i<n;i++)cout<<s[i];
cout<<" ";
cout<<"a"<<endl;
}
else if(s[0]=='b'&&s[n]=='b'){
cout<<"b"<<" ";
for(ll i=1;i<n;i++)cout<<s[i];
cout<<" ";
cout<<"b"<<endl;
}
else if(s[0]=='a'&&s[n]=='b'){
cout<<"a"<<" ";
if(s[1]=='a'){
cout<<"a"<<" ";
for(ll i=2;i<=n;i++)cout<<s[i];
cout<<endl;
}
else{
for(ll i=1;i<n;i++)cout<<s[i];
cout<<" ";
cout<<"b"<<endl;
}
}
else{
cout<<"b"<<" ";
if(s[1]=='a'){
cout<<"a"<<" ";
for(ll i=2;i<=n;i++)cout<<s[i];
cout<<endl;
}
else{
for(ll i=1;i<n;i++)cout<<s[i];
cout<<" ";
cout<<"a"<<endl;
}
}
}
return 0;
}
B. Gardener and the Array
题意
题目链接
就是给你一个数组,这个数组中的数都是大数,用二进制位表示。问能不能选出两个不同的子数组,使得这两个子数组各自所有的数按位或的值相同。
思路
由于是大数,而且数据范围不够明确,这个题需要开动态数组和键值对存储。说白了,对于某一个大数的每一位,都满足:如果这一位是1,而且其他大数中至少有一个在这一位上也是1,那就符合题意。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 1e5 + 5;
vector<ll> v[maxn];
map<ll,ll> cnt;
int main(){
//ios::sync_with_stdio(0);
//cin.tie(0),cout.tie(0);
ll t;
scanf("%lld",&t);
while(t--){
ll n;
scanf("%lld",&n);
for(ll i=1;i<=n;i++)v[i].clear();
cnt.clear();
ll bit = -1e9;
for(ll i=1;i<=n;i++){
ll k;scanf("%lld",&k);
for(ll j=0;j<k;j++){
ll x;scanf("%lld",&x);
bit=max(bit,x);
v[i].push_back(x);
}
}
for(ll i=1;i<=n;i++){
for(auto j: v[i]){
cnt[j]++;
}
}
bool flag=false;
for(ll i=1;i<=n;i++){
bool f=true;
for(auto j: v[i]){
if(cnt[j]==1){
f=false;break;
}
}
if(f==true){
flag=true;
break;
}
}
if(flag==true)printf("Yes\n");
else printf("No\n");
}
return 0;
}
C. Interesting Sequence
题意
题目链接
求出最小的
m
≥
n
m≥n
m≥n,使得
n
&
(
n
+
1
)
&
…
…
&
m
=
x
n\&(n+1)\&……\&m=x
n&(n+1)&……&m=x。
思路
分四种情况讨论,分别看对于某一位
i
i
i的具体情况:
①
n
i
=
0
,
x
i
=
0
n_{i}=0,x_{i}=0
ni=0,xi=0,对答案不构成影响。
②
n
i
=
1
,
x
i
=
0
n_{i}=1,x_{i}=0
ni=1,xi=0,说明在
[
n
,
m
]
[n,m]
[n,m]范围内,至少存在一个数的第
i
i
i位是
0
0
0。
③
n
i
=
0
,
x
i
=
1
n_{i}=0,x_{i}=1
ni=0,xi=1,无解,输出
−
1
-1
−1。
④
n
i
=
1
,
x
i
=
1
n_{i}=1,x_{i}=1
ni=1,xi=1,说明在
[
n
,
m
]
[n,m]
[n,m]范围内,所有数的第
i
i
i位都是
1
1
1。
对于④,一旦存在有更高位的②,那么就不合法。因为在逐个 & \& &的过程中必然会遇上 0 0 0的情况。即高位有进位,那么低位也必然都变成 0 0 0。
所有②的最高位是影响最大的,我们需要找到这个位。然后判断是否在比这个位小的位上存在④。如果存在,输出 − 1 -1 −1。
假设最高位的②是
p
o
s
pos
pos,那么最终答案就是:
原来
n
n
n上位置比
p
o
s
pos
pos大的
p
o
s
+
1
pos+1
pos+1这一位置
1
1
1,这一位前面都是
0
0
0,后面就是原来
n
n
n的二进制位数值。如果原来
n
n
n上的
p
o
s
+
1
pos+1
pos+1这一位就是
1
1
1,那么
p
o
s
pos
pos位是
0
0
0,
p
o
s
+
1
pos+1
pos+1位必然产生进位,那么这一位原来的
1
1
1必然成为
0
0
0,就不满足④,因此输出
−
1
-1
−1。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 64 + 5;
ll bitn[maxn],bitx[maxn],m[maxn];
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
ll t;
cin>>t;
while(t--){
ll n,x;
cin>>n>>x;
memset(bitn,0,sizeof(bitn));
memset(bitx,0,sizeof(bitx));
ll cntn=0,cntx=0;
ll nn=n,xx=x;
while(nn){
bitn[cntn++]=(nn&1);
nn=nn>>1;
}
while(xx){
bitx[cntx++]=(xx&1);
xx=xx>>1;
}
bool flag=true;
for(ll i=0;i<max(cntn,cntx);i++){
if(bitn[i]==0&&bitx[i]==1){
flag=false;
break;
}
}
ll pos=-1;
for(ll i=0;i<max(cntn,cntx);i++){
if(bitn[i]==1&&bitx[i]==0)pos=i;
}
for(ll i=0;i<max(cntn,cntx);i++){
if(bitn[i]==1&&bitx[i]==1){
if(pos>i){
flag=false;
break;
}
}
}
if(flag==false)cout<<-1<<endl;
else{
if(pos==-1)cout<<n<<endl;
else if(bitn[pos+1]==1&&bitx[pos+1]==1)cout<<-1<<endl;
else{
for(ll i=0;i<=pos;i++)m[i]=0;
m[pos+1]=1;
if(pos+1==cntn)cntn++;
for(ll i=pos+2;i<cntn;i++)m[i]=bitn[i];
ll ans = 0;
for(ll i=0;i<cntn;i++)ans+=(1LL<<i)*m[i];
cout<<ans<<endl;
}
}
}
return 0;
}
心得
这场比赛的题风格很相似,基本就是二进制位或者两个字符的操作,非常考验分类讨论和梳理脉络的思想。每个题细节都很多,比赛时需要在草稿纸上对每种情况逐个理清,多分析几个样例。