比赛链接
官方题解
Problem A1. Gardener and the Capybaras (easy version)
直接按照题意模拟即可
时间复杂度 ,因为有
的常数,所以可以通过
#include <bits/stdc++.h>
using namespace std;
const int N(200100);
int T,n;
char c[N];
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar())
if(ch=='-')
RR=-1;
for(;isdigit(ch);ch=getchar())
FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
int main(){
T=read();
while(T--){
scanf("%s",c+1);
n=strlen(c+1);
bool flg=0;
for(int i=2;i<=n;i++){
for(int j=i;j<n;j++){
string s1="";
for(int k=1;k<i;k++)
s1+=c[k];
string s2="";
for(int k=i;k<=j;k++)
s2+=c[k];
string s3="";
for(int k=j+1;k<=n;k++)
s3+=c[k];
if((s1<=s2&&s3<=s2)||(s2<=s1&&s2<=s3)){
cout<<s1<<' '<<s2<<' '<<s3<<'\n';
flg=1;
break;
}
}
if(flg)
break;
}
if(!flg)
puts(":(");
}
return 0;
}
Problem A2. Gardener and the Capybaras (hard version)
这道题是上道题的加强版
考虑分类讨论
1. 首位是a
首先思考让中间的序列开头为b,末尾的序列开头为a,这样是最简单的情况
找到第一个b出现的位置k1,最后一个a出现的位置k2
如果k1<k2,那么一定可以构造出 1到k1-1,k1到k2-1,k2到n这三段序列符合要求
如果k1>k2,那么这个序列的形式一定是 a…ab…b
那么只要序列分别是 a…ab…b,b,b就可以符合要求
这里需要考虑特殊情况b的个数如果<2
上述构造方式也成立,因为中间的序列一定为a,是最小的
2. 首位是b
构造方式与首位是a类似,不多加赘述
时间复杂度
//考场代码巨丑无比
#include <bits/stdc++.h>
using namespace std;
const int N(200100);
int T,n;
char a[N];
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar())
if(ch=='-')
RR=-1;
for(;isdigit(ch);ch=getchar())
FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
int main(){
T=read();
while(T--){
scanf("%s",a+1);
n=strlen(a+1);
if(a[1]=='a'){
int i=1,j=n;
for(;i<=n;i++)
if(a[i]=='b')
break;
for(;j;j--)
if(a[j]=='a')
break;
if(i>j){
if(n-j>=2){//a..abb..b
for(int k=1;k<=n-2;k++)
cout<<a[k];
cout<<" b b";
}
else{
for(int k=1;k<=n-2;k++)
cout<<a[k];
cout<<' '<<a[n-1]<<' '<<a[n];
}
}
else
for(i=1;i<=n;i++)
if(a[i]=='b'){
for(j=1;j<i;j++)
cout<<a[j];
cout<<' ';
bool flg=0;
for(j=i;j<=n;j++){
if(a[j]=='a'&&!flg){
cout<<' ';
flg=1;
}
cout<<a[j];
}
break;
}
}
if(a[1]=='b'){
int i=1,j=n;
for(;i<=n;i++)
if(a[i]=='a')
break;
for(;j;j--)
if(a[j]=='b')
break;
if(i>j){
if(n-j>=2){
for(int k=1;k<=n-2;k++)
cout<<a[k];
cout<<" a a";
}
else{
cout<<a[1]<<' ';
for(int k=2;k<=n-1;k++)
cout<<a[k];
cout<<' '<<a[n];
}
}
else
for(i=1;i<=n;i++)
if(a[i]=='a'){
for(j=1;j<i;j++)
cout<<a[j];
cout<<' ';
bool flg=0;
for(j=i;j<=n;j++){
if(a[j]=='b'&&!flg){
cout<<' ';
flg=1;
}
cout<<a[j];
}
break;
}
}
puts("");
}
return 0;
}
Problem B. Gardener and the Array
我们考虑两个集合S1,S2,如果满足 f(S1)=f(S2)
那么它的形式一定是:
我们考虑他们不交的部分,这一部分中出现的所有数一定在另一个集合中出现过
所以其中必有一组数S3每个数都在总的集合中出现了不少于2次
那么构造 f(总集合)=f(总集合去掉S3)
所以只要集合中有一组数中每个数都在总集合中出现了不少于2次,答案就是 Yes
否则就是 No
这里每次清空要当心一下,要记录一下被修改过的位置,否则复杂度就假了
时间复杂度
#include <bits/stdc++.h>
using namespace std;
const int N(100100),S(200100);
int T,n,cnt[S];
vector<int> vec[N];
set<int> used;
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar())
if(ch=='-')
RR=-1;
for(;isdigit(ch);ch=getchar())
FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
int main(){
T=read();
while(T--){
for(set<int>::iterator it=used.begin();it!=used.end();it++)
cnt[*it]=0;
used.clear();
n=read();
for(int i=1,k;i<=n;i++){
vec[i].clear();
k=read();
while(k--){
int x=read();cnt[x]++;
vec[i].push_back(x);
used.insert(x);
}
}
bool ok=0;
for(int i=1;i<=n;i++){
bool flg=1;
for(int j=0;j<vec[i].size();j++)
if(cnt[vec[i][j]]==1){
flg=0;
break;
}
if(flg){
puts("Yes");
ok=1;
break;
}
}
if(!ok)
puts("No");
}
return 0;
}
Problem C. Interesting Sequence
很明显对于每一位分开来进行考虑
如果n的第i位是0,且x的第i位是1,无解
如果n的第i位是1,且x的第i位是0,答案一定>=最小的第i位是0且>=n的数
如果n的第i位是1,且x的第i位是1,答案一定<最小的第i位是0的>=n的数
时间复杂度
#include <bits/stdc++.h>
#define int unsigned long long
using namespace std;
int T,n,x;
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar())
if(ch=='-')
RR=-1;
for(;isdigit(ch);ch=getchar())
FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
signed main(){
T=read();
while(T--){
n=read(),x=read();
bool flg=1;
for(int i=0;i<64&&flg;i++)
if(!(n>>i&1ll)&&(x>>i&1ll)){
puts("-1");
flg=0;
}
if(!flg)
continue;
int maxi=5e18,mini=n;
for(int i=0;i<64;i++)
if(n>>i&1ll){
if(x>>i&1ll){
int res=0;
for(int j=63;j>i;j--){
int c=n>>j&1ll;
res+=c*(1ll<<j);
}
res+=(1ll<<(i+1));
// cout<<i<<' '<<res<<'\n';
maxi=min(maxi,res-1);
}
else{
int res=0;
for(int j=63;j>i;j--){
int c=n>>j&1ll;
res+=c*(1ll<<j);
}
res+=(1ll<<(i+1));
// cout<<i<<' '<<res<<'\n';
mini=max(mini,res);
}
}
if(mini>maxi)
puts("-1");
else
printf("%lld\n",mini);
}
return 0;
}
Problem D. Friendly Spiders
这道题一条一条连边时间和空间都不够
我们考虑优化建图
我们可以对每一个质因子建一个点
且将每个点与它的质因子之间连双向边
然后在跑bfs就可以了
这个建图优化还是比较基础的
时间复杂度,
大约是质因子的密度
#include <bits/stdc++.h>
using namespace std;
const int N(600100),inf(0x3f3f3f3f);
int n,s,t,a[N],dp[N],pre[N];
int cnt,v[N],prime[N];
vector<int> vec[N];
queue<int> que;
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar())
if(ch=='-')
RR=-1;
for(;isdigit(ch);ch=getchar())
FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
void print(int x){
if(x==0)
return;
print(pre[x]);
if(x<=n)
printf("%d ",x);
}
int main(){
for(int i=2;i<=300000;i++){
if(!v[i])
v[i]=prime[++cnt]=i;
for(int j=1;j<=cnt&&i<=300000/prime[j];j++){
v[prime[j]*i]=prime[j];
if(prime[j]==v[i])
break;
}
}
n=read();
for(int i=1;i<=n;i++)
a[i]=read();
for(int i=1;i<=n;i++){
for(int j=1;prime[j]*prime[j]<=a[i];j++){
if(a[i]%prime[j]==0){
while(a[i]%prime[j]==0)
a[i]/=prime[j];
vec[i].push_back(prime[j]+n);
vec[prime[j]+n].push_back(i);
}
}
if(a[i]>1){
vec[i].push_back(a[i]+n);
vec[a[i]+n].push_back(i);
}
}
s=read(),t=read();
memset(dp,0x3f,sizeof(dp));
que.push(s);dp[s]=1;
while(!que.empty()){
int u=que.front();
que.pop();
for(int i=0;i<vec[u].size();i++){
int v=vec[u][i];
if(dp[u]+1<dp[v]){
dp[v]=dp[u]+1,pre[v]=u;
que.push(v);
}
}
}
if(dp[t]==inf)
puts("-1");
else{
printf("%d\n",(dp[t]+1)/2);
print(t);
}
return 0;
}
Problem E. The Human Equation
这道题乍一看没有任何思路
我们考虑转化数组,可以试一下类似差分,前缀和一类的
这里采用把原数组转化为前缀和数组
对于样例 2 -4 3 -5 4 1
前缀和为 2 -2 1 -4 0 1
我们将 2 3 4 1 奇数位+1,偶数位-1可以变成 3 -4 2 -5 5 0
前缀和为 3 -1 1 -4 1 1
我们发现前缀和数组中[1,2],[5,5]都+1
多玩几个数据之后可以发现一次操作可以转化成一些区间一起+1或-1
问题转化成了通过一些区间一起+1或-1,最少的操作数使得前缀和数组为0
稍加思考会发现答案是前缀和数组中最大的正数+最小的负数的绝对值
时间复杂度
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N(200100);
int n,a[N];
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar())
if(ch=='-')
RR=-1;
for(;isdigit(ch);ch=getchar())
FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
void work(){
n=read();
for(int i=1;i<=n;i++)
a[i]=read(),a[i]+=a[i-1];
int maxi=0,mini=0;
for(int i=1;i<=n;i++){
maxi=max(maxi,a[i]);
mini=min(mini,a[i]);
}
printf("%lld\n",maxi-mini);
}
signed main(){
int T=read();
while(T--)
work();
return 0;
}
Problem F. Laboratory on Pluto
不会~