牛客周赛 Round 66 个人题解(A~G)
牛客周赛 Round 66 个人题解
A-小苯吃糖果_牛客周赛 Round 66
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
int a[5];
void solve(){
for(int i=1;i<=3;i++) cin>>a[i];
sort(a+1,a+4);
int ans=max(a[1]+a[2],a[3]);
cout<<ans<<endl;
}
int main(){
std::ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T=1;
while(T--) solve();
return 0;
}
B-小苯的排列构造_牛客周赛 Round 66
解题思路
- 直接构造排列n,n-1,n-2,...,1即可,这样构造和均为n+1
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
void solve(){
int n;cin>>n;
for(int i=n;i>=1;i--) cout<<i<<" ";
cout<<endl;
}
int main(){
std::ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--) solve();
return 0;
}
C-小苯的最小整数_牛客周赛 Round 66
解题思路
- 数字中不存在0,直接暴力处理就可以
- 这里嫌麻烦用了字符串函数stoll作用是将一个字符串转化为long long的数字
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
void solve(){
string s;cin>>s;
int n=s.size();
int ans=stoll(s);
string tmp=s;
for(int i=0;i<n;i++){
tmp.erase(0,1);
tmp+=s[i];
int num=stoll(tmp);
ans=min(ans,num);
}
cout<<ans<<endl;
}
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--) solve();
return 0;
}
D-小苯的蓄水池(easy)_牛客周赛 Round 66
解题思路
- easy版本n,m都是1000,直接暴力修改所有水池联通情况,查询x水池只需找到包含x水池的最左端点和最右端点即可,时间复杂度O(nm)
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2010;
int a[N];
int vis[N];
void solve(){
int n,m;cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
while(m--){
int op;cin>>op;
if(op==1){
int x,y;cin>>x>>y;
for(int i=x;i<=y;i++) vis[i]=1;
}
else if(op==2){
int id;cin>>id;
int tot=1;
int sum=a[id];
if(vis[id]==0){
cout<<sum<<endl;
continue;
}
for(int i=id-1;i>=1;i--){
if(vis[i]){
tot++;
sum+=a[i];
}
else break;
}
for(int i=id+1;i<=n;i++){
if(vis[i]){
tot++;
sum+=a[i];
}
else break;
}
double num=(1.0*sum)/(1.0*tot);
printf("%.9lf\n",num);
}
}
}
signed main(){
// std::ios::sync_with_stdio(false);
// cin.tie(0);cout.tie(0);
int T=1;
while(T--) solve();
return 0;
}
E-小苯的蓄水池(hard)_牛客周赛 Round 66
解题思路
- n,m均为2e5,考虑优化,这里的连通性问题可以用并查集较好维护,赛时想到了并查集但是没算好复杂度,均摊下来其实是O(n)的
- 我们考虑每次都往较大的右端点做merge,[l,r]合并时我们将所有点最终都merge到r结点
- 然后我们眺节点时只需令l=find(l),这样就可以实现快速的查找到根节点
- 这样的话我们只需再对每一个连通块维护一个sz,和一个sum,就可以快速计算
- 所有合并均摊复杂度为O(n)的时间复杂度
- 苯环老师说这是链式并查集
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
int a[N],sz[N],fa[N];
int find(int x){
if(x!=fa[x]) return fa[x]=find(fa[x]);
else return fa[x];
}
void merge(int x,int y){
int px=find(x),py=find(y);
if(px!=py){
if(px<py) swap(px,py);
fa[py]=px;
sz[px]+=sz[py];
a[px]+=a[py];
}
}
void solve(){
int n,q;cin>>n>>q;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
fa[i]=i;
sz[i]=1;
}
while(q--){
int op;cin>>op;
if(op==1){
int l,r;cin>>l>>r;
while(l<r){
merge(l,l+1);
l=find(l);
}
}
else{
int id;cin>>id;
int px=find(id);
double ans=1.0*a[px]/sz[px];
cout<<fixed<<setprecision(10)<<ans<<endl;
}
}
}
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T=1;
while(T--) solve();
return 0;
}
F-小苯的字符提前_牛客周赛 Round 66
解题思路
-
guess题,观察题目性质,第k小的字典序串首先我们可以确定的是第k小字符串的首字母为什么,先处理出来
-
接下来需要处理的是对于各个为需要转移到第一位的首字母在原字符串的所有位置,我们发现,进行move操作以后,该字符前的前缀不变,后缀除了下一个字符也都不变
-
例如accabcsbg,move(5)以后为baccacsbg,[acca],[sbg]这两个前后缀都是没有改变的,而移动字符的下一个字符就会占据原字符位置
-
进一步观察性质我们发现,假设s[i]为move的字符,s[i+1]>s[i]时move(i)会大于之后的所有move串,s[i+1]<s[i]时move(i)会小于之后所有的move串,(这里可以画个图思考一下)
-
怎么维护这个过程呢?我们倒着枚举,用一个双端队列维护,如果s[i+1]>s[i]则push_back,否则push_front
-
接下来只需pop掉除了需要move的字符的前面所有move串的个数sum个,再pop掉剩下的个数,满足队头为第 k小即可
-
细节见代码
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
void solve(){
int n,k;cin>>n>>k;
string s;cin>>s;
s=" "+s;
map<int,int> mp;
for(int i=1;i<=n;i++){
mp[s[i]-'a'+1]++;
}
int idx=-1,sum=0;
for(int i=1;i<=26;i++){
if(sum+mp[i]>=k){
idx=i;
break;
}
sum+=mp[i];
}
vector<int> nxt(n+5);
//处理字符串右边第一个不同字符的位置
for(int i=n;i>=1;i--){
if(i<n && s[i]==s[i+1]) nxt[i]=nxt[i+1];
else nxt[i]=i+1;
}
deque<int> q;
for(int i=n;i>=1;i--){
int id=s[i]-'a'+1;
if(id!=idx) continue;
int x=nxt[i];
if(x<=n && s[i]<s[x]) q.push_back(i);
else q.push_front(i);
}
for(int i=1;i<=k-sum-1;i++) q.pop_front();
int top=q.front();
char ch=s[top];
string ans1=s.substr(1,top-1);
string ans2=s.substr(top+1,n-top);
cout<<ch<<ans1<<ans2<<endl;
}
int main(){
std::ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--) solve();
return 0;
}
G-小苯的数位MEX_牛客周赛 Round 66
解题思路
- 数位dp+状态压缩,但是没做过记忆化搜索写法的数位dp,以此题做引子,学习了一下
- 数位dp内容可参考该文章 算法学习笔记(22):数位DP(数位动态规划) - 知乎
- 知道数位dp的写法剩下的部分就不会特别难了,详情可以见代码细节
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int f[15][1200],a[15];
int mex;
int dfs(int pos,int st,bool limit,bool lead0){
if(pos==0){
if(lead0) return 0;//全是前导0
for(int i=0;i<mex;i++){
if((st>>i & 1)==0) return 0;//mex以下的数位需要存在
}
return 1;
}
if(!lead0 && !limit && f[pos][st]!=-1) return f[pos][st];
int up=limit?a[pos]:9;
int res=0;
for(int i=0;i<=up;i++){
int _st=st|(1ll<<i);
if(lead0 && i==0) _st=0;
//当前数不为前导0且为mex
if(i==mex && (lead0 & (i==0))==0) continue;
res+=dfs(pos-1,_st,limit && i==up,lead0 && i==0);
}
if(!limit && !lead0) f[pos][st]=res;
return res;
}
int calc(int x){
memset(f,-1,sizeof(f));
int len=0;
while(x){
a[++len]=x%10;
x/=10;
}
return dfs(len,0,1,1);
}
void solve(){
int x,k;cin>>x>>k;
int l=x,r=x+k;
for(int i=10;i>=0;i--){
mex=i;
int ans1=calc(r);
int ans2=calc(l-1);
if(ans1>ans2){
cout<<i<<" "<<ans1-ans2<<endl;
return;
}
}
}
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--) solve();
return 0;
}