Codeforces Round #616 题解
A题
我们注意到如果存在两个奇数,那么就能满足题目条件,所以我们就从头寻找两个奇数,没有的话就是无解
#include<iostream> #include<algorithm> #include<cstdio> #include<cmath> #include<vector> #include<string> #include<cstring> #include<map> using namespace std; typedef long long ll; const int N=600010; int pos[N]={0}; int main(){ int t; cin>>t; while(t--){ int n; string s; cin>>n; cin>>s; if(n==1){ cout<<-1<<endl; continue; } int cnt=0; int i; for(i=0;i<s.size();i++){ if(cnt==2) break; if((s[i]-'0')%2){ pos[cnt++]=s[i]-'0'; } } if(cnt<2) cout<<-1<<endl; else cout<<pos[0]<<pos[1]<<endl; } }
B题
可以贪心的想,只要将串设计为0 1 2 3 ……3 2 1 0
这种形式的话是最容易的满足条件的,因为每个数只要大于该数所在位置的下标就行,其他方法都没有这样好
因此我们可以设计两个哨兵,一个l从开始枚举直到第一个不满足条件的,一个r从后面往前枚举
如果l>=r说明满足条件,输出yes因为两边都满足这样的条件,我们只需要取l=r处为中间点,两边按自己的规律枚举即可。
注意判断完全递增和完全递减序列
另外,我的代码有特判一位数的情况,每个一位数都可以,和两位数并且两个都是0的情况,这是不行的,其他两位数都可以
其实不用特判。
我的代码中对每个数都加了1,因为我是从第一位开始枚举的,从第0位开始枚举的不用+1
#include<iostream> #include<algorithm> #include<cstdio> #include<cmath> #include<vector> #include<string> #include<cstring> #include<map> using namespace std; typedef long long ll; const int N=3e5+10; int a[N]; int main(){ int t; cin>>t; while(t--){ int n; cin>>n; int i; for(i=1;i<=n;i++){ cin>>a[i]; a[i]+=1; } if(n==1){ cout<<"Yes"<<endl; } else if(n==2){ if(a[1]==1&&a[2]==1) cout<<"No"<<endl; else cout<<"Yes"<<endl; } else{ int p1=-2; int p2=n+2; for(i=1;i<=n;i++){ if(a[i]<i){ p1=i-1; break; } } for(i=n;i>=1;i--){ if(a[i]<n-i+1){ p2=i+1; break; } } int flag=0; if(p2==n+2||p1==-2) flag=1; else if(p1>=p2) flag=1; if(flag==1) cout<<"Yes"<<endl; else cout<<"No"<<endl; } } }
C题
本题看数据范围发现可以满足O(N^2)的算法,所以我们可以直接上暴力,下面有几个注意点
1.k要更新为min(m-1,k),因为即使k再大我们控制后面的人也没有意义
2.我们要明确一点,控制的人越多越好,这是一种贪心的思路,这也是很显然的,这样你就可以根据情况尽可能的将最大的留给自己
3.我们需要枚举两维,代表的意思是我控制k个人中取前面的人数,和不受控制的人选前面的人数,这样就能枚举出所有情况
tips:第二维可以使用线段树维护,这样可以降低复杂度(虽然本题数据可以暴力水过),有兴趣的同学可以取cf官网上查询官方题解
枚举过程见代码注释
#include<iostream> #include<algorithm> #include<cstdio> #include<cmath> #include<vector> #include<string> #include<cstring> #include<map> using namespace std; typedef long long ll; const int N=3e5+10; const int inf=0x3f3f3f3f; int a[N]; int main(){ int n; int i; int m,k; int t; cin>>t; while(t--){ cin>>n>>m>>k; k=min(m-1,k); for(i=0;i<n;i++){ cin>>a[i]; } int ans=-inf; int j; int tmp; for(i=0;i<=k;i++){//k个人中我选i个取前面 tmp=inf; for(j=0;j<m-k;j++){//剩下的m-1-k人中有j个在前面 tmp=min(tmp,max(a[i+j],a[i+j+(n-m)])); //轮到我取的时候我取前面还是最后面的最大值的最小值 } ans=max(ans,tmp); } cout<<ans<<endl; } }
D题
本题是一道思维构造题目,另外可以使用莫队算法记录答案。
题意是对获得的子串,重新构造看能否成为irr的情况
1.如果只有一个元素,那么是满足的,因为题目要求k>=2
2.如果只有两种元素,且开头和末尾不等,那也满足条件,只需对调开头和末尾
当开头和末尾都相等,那么就不满足,因为首先我们知道原先他们的字符总数和各个字符总数是相同的,举个例子 aaabbba
那么我们重新构造的子串有下列可能
b------a
b------b
a------b
a------a
对于第一种,只需要断点设在倒数第二位
对于第二种,断点设在第一个满足前x个是alagram的位置,这样前后两段依旧满足
对于第三种,断点设在第二位
第四种天然满足
所以证明。
3.如果有不同的三个字符,显然都可以构造
#include<iostream> #include<algorithm> #include<cstdio> #include<cmath> #include<vector> #include<string> #include<cstring> #include<map> using namespace std; typedef long long ll; const int N=3e5+10; int pos[N]; int cnt[N]; int b[N]; int ans[N]; char s[N]; struct node{ int l,r; int k; }q[N]; bool cmp(node a,node b){ if(pos[a.l]==pos[b.l]) return a.r<b.r; return pos[a.l]<pos[b.l]; } int main(){ int m; scanf("%s",s+1); cin>>m; int i; int n=strlen(s+1); int block=sqrt(n); for(i=1;i<=n;i++){ b[i]=s[i]-'a'+1; pos[i]=(i-1)/block+1; } int l=1; int r=0; for(i=1;i<=m;i++){ scanf("%d%d",&q[i].l,&q[i].r); q[i].k=i; } sort(q+1,q+1+m,cmp); int res=0; for(i=1;i<=m;i++){ if(q[i].l==q[i].r){ ans[q[i].k]=1; continue; } while(l<q[i].l) res-=(!--cnt[b[l++]]); while(l>q[i].l) res+=(!cnt[b[--l]]++); while(r<q[i].r) res+=(!cnt[b[++r]]++); while(r>q[i].r) res-=(!--cnt[b[r--]]); ans[q[i].k]=((res==2&&b[q[i].l]!=b[q[i].r]||res>2)?1:-1); } for(i=1;i<=m;i++) if(ans[i]==-1) printf("NO\n"); else puts("YES"); }