牛客周賽 Round 66 題解
牛客周賽 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;
}