竞赛讨论区 > 【题解】牛客小白月赛131题解
头像
比那名居的桃子
编辑于 昨天 21:08 北京
+ 关注

【题解】牛客小白月赛131题解

小白月赛 131 题解

A. 小红的7

知识点数学模拟字符串

预估难度800

,以此类推,我们发现每个 对应的循环节的首位数字是完全不同的。

参考代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main(){
    ios::sync_with_stdio(false);cin.tie(0);
    int _;cin>>_;
    while(_--){
        string s;cin>>s;
        if(s[0]=='1')cout<<1<<"\n";
        else if(s[0]=='2')cout<<2<<"\n";
        else if(s[0]=='4')cout<<3<<"\n";
        else if(s[0]=='5')cout<<4<<"\n";
        else if(s[0]=='7')cout<<5<<"\n";
        else if(s[0]=='8')cout<<6<<"\n";
    }
}

B. 小红的回文串

知识点贪心字符串回文串

预估难度1100

原串已经是回文串,如果想要删除字符后依然是回文串,我们必须且只能删除位于回文串正中心的那些连续相同的字符。

为什么呢?因为一旦删去偏离中心的字符,左右两边对应的对称点必然会发生错位,导致新串不再回文。所以我们只需找到原串最中间的字符块,统计其中有多少个字符与正中心字符相同即可,有多少个就有多少种删除方案。

参考代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main(){
    ios::sync_with_stdio(false);cin.tie(0);
    int _;cin>>_;
    while(_--){
        int n,i,ans=0;
        string s;
        cin>>n>>s;
        if(n%2==1){
            ans=1;
            for(i=n/2-1;i>=0;i--){
                if(s[i]==s[n/2])ans+=2;
                else break;
            }
        }else{
            if(s[n/2-1]==s[n/2]){
                ans=2;
                for(i=n/2-2;i>=0;i--){
                    if(s[i]==s[n/2])ans+=2;
                    else break;
                }
            }
        }
        cout<<ans<<"\n";
    }
}

C. 小红的gcd位运算构造

知识点位运算构造数论

预估难度1000

要求 ,最容易想到的互质数对自然是包含 或者某个不被 整除的质数的次幂。

如果 是偶数,那么它的二进制第 位必然是 ,我们直接令 即可,显然

如果 是奇数,那么 不能被 整除,即 不包含因子 。我们只需要找到 二进制中最低位的 (设在第 位),令 即可。由于 的次幂,而 是奇数,二者必然互质;同时 的第 位为 ,自然有

参考代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main(){
    ios::sync_with_stdio(false);cin.tie(0);
    int _;cin>>_;
    while(_--){
        int x,y=1;
        cin>>x;
        while((x&y)!=0)y<<=1;
        cout<<y<<"\n";
    }
}

D. i在西元前

知识点数学复数模拟

预估难度1400

判定等比数列的核心是存在一个公比 使得 。利用等比数列的性质将其转化为交叉相乘:。题目保证了元素不为 ),因此这完全等价于判断相邻两项的商是否与前两项的商一致。

参考代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[202020][2];
signed main(){
    ios::sync_with_stdio(false);cin.tie(0);
    int _;cin>>_;
    while(_--){
        int n,i,ok=1;
        cin>>n;
        for(i=0;i<n;i++)cin>>a[i][0]>>a[i][1];
        for(i=1;i<n-1;i++){
            int r1=a[i+1][0]*a[0][0]-a[i+1][1]*a[0][1];
            int i1=a[i+1][0]*a[0][1]+a[i+1][1]*a[0][0];
            int r2=a[i][0]*a[1][0]-a[i][1]*a[1][1];
            int i2=a[i][0]*a[1][1]+a[i][1]*a[1][0];
            if(r1!=r2||i1!=i2){ok=0;break;}
        }
        if(ok)cout<<"Yes\n";
        else cout<<"No\n";
    }
}

E. 小红打游戏

知识点动态规划背包问题混合背包

预估难度1500

把这两类关卡分开处理。既然普通关卡(后 个)的选取没有次数限制,我们可以先用一个标准的二维完全背包(正向遍历容量),求出仅使用普通关卡时获取资源的最小花费。

处理完普通关卡后,我们再去处理限定关卡。由于限定关卡每关至多使用一次,所以用标准的二维 01 背包(逆向遍历容量)去在这个已经包含普通关卡的基础 DP 数组上继续转移。注意这里的容量可以对 ,因为溢出的部分也可以等效认为是满足了目标。全部算完后查 即可。

参考代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[10101],b[10101],c[10101];
int dp[1010][1010];
signed main(){
    ios::sync_with_stdio(false);cin.tie(0);
    int _;cin>>_;
    while(_--){
        int n,k,x,y,i,cx,cy,ans=1e18;
        cin>>n>>k>>x>>y;
        for(i=1;i<=n;i++)cin>>a[i]>>b[i]>>c[i];
        for(cx=0;cx<=x;cx++)for(cy=0;cy<=y;cy++)dp[cx][cy]=1e18;
        dp[0][0]=0;
        // 普通关卡(完全背包,正向遍历)
        for(i=k+1;i<=n;i++){
            for(cx=0;cx<=x;cx++){
                for(cy=0;cy<=y;cy++){
                    if(dp[cx][cy]==1e18)continue;
                    int nx=min(x,cx+a[i]);
                    int ny=min(y,cy+b[i]);
                    dp[nx][ny]=min(dp[nx][ny],dp[cx][cy]+c[i]);
                }
            }
        }
        // 限定关卡(01背包,逆向遍历)
        for(i=1;i<=k;i++){
            for(cx=x;cx>=0;cx--){
                for(cy=y;cy>=0;cy--){
                    if(dp[cx][cy]==1e18)continue;
                    int nx=min(x,cx+a[i]);
                    int ny=min(y,cy+b[i]);
                    dp[nx][ny]=min(dp[nx][ny],dp[cx][cy]+c[i]);
                }
            }
        }
        ans=dp[x][y];
        if(ans==1e18)cout<<-1<<"\n";
        else cout<<ans<<"\n";
    }
}

F. 小红的等比数列

知识点数学构造分类讨论

预估难度2300

由于原数组全为整数,且插入的元素也必须为整数,等比数列的公比 必然是一个有理数

等比数列的一个重要性质是:由于公比的绝对值必然 ,所以如果我们直接对整个数组按绝对值排序,那么排序后的序列必然已经排好了等比的先后顺序。

因此我们首先将数组按照绝对值进行排序。排序后,合法的等比数列必然满足处处都有

特判 的情况:要么在两端延长(),要么在中间插入(前提是 且存在整数平方根)。

特判所有元素绝对值相同的情况:此时公比必然是 。只需统计正数和负数的个数,看是否能通过插入一个数满足全同(公比 )或正负交替(公比 )。

对于一般情况,我们扫一遍排序后的数组,找到第一个不满足 的位置 。如果找不到这样的 ,说明原序列已经是一个完整的等比数列,我们只需尝试在首部或尾部追加一个合法整数即可。

如果找到了 ,说明“缺失的那个元素”必然在 附近。我们只需要利用 附近(如 等)依然保持着等比关系的元素,推算出缺失的元素 (例如 等),然后将 插入到序列中,再用 的时间全局 check 一遍 是否全部成立即可。

参考代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,a[202020],b[202020];
bool cmp(int x,int y){return abs(x)<abs(y);}
bool go(int k,int x){
    int i;
    for(i=0;i<k;i++)b[i]=a[i];
    b[k]=x;
    for(i=k;i<n;i++)b[i+1]=a[i];
    for(i=1;i<n;i++)if(b[i]*b[i]!=b[i-1]*b[i+1])return 0;
    cout<<"Yes\n";
    for(i=0;i<=n;i++)cout<<b[i]<<" ";
    cout<<"\n";
    return 1;
}
void solve(){
    int i,j,k,x,y;
    cin>>n;
    for(i=0;i<n;i++)cin>>a[i];
    sort(a,a+n,cmp);
    if(n==1){
        cout<<"Yes\n"<<a[0]<<" "<<a[0]<<"\n";
        return;
    }
    if(n==2){
        if(a[1]*a[1]%a[0]==0){
            cout<<"Yes\n"<<a[0]<<" "<<a[1]<<" "<<a[1]*a[1]/a[0]<<"\n";
            return;
        }
        if(a[0]*a[0]%a[1]==0){
            cout<<"Yes\n"<<a[0]*a[0]/a[1]<<" "<<a[0]<<" "<<a[1]<<"\n";
            return;
        }
        if(a[0]*a[1]>0){
            x=round(sqrt(a[0]*a[1]));
            for(k=max(0LL,x-2);k<=x+2;k++){
                if(k*k==a[0]*a[1]){
                    cout<<"Yes\n"<<a[0]<<" "<<k<<" "<<a[1]<<"\n";
                    return;
                }
            }
        }
        cout<<"No\n";
        return;
    }
    if(abs(a[0])==abs(a[n-1])){
        int v=abs(a[0]),m=n+1,p=0,q=0;
        for(i=0;i<n;i++)if(a[i]>0)p++;else q++;
        if(!q){
            cout<<"Yes\n";
            for(i=0;i<m;i++)cout<<v<<" ";
            cout<<"\n";
            return;
        }
        if(!p){
            cout<<"Yes\n";
            for(i=0;i<m;i++)cout<<-v<<" ";
            cout<<"\n";
            return;
        }
        for(int d:{v,-v}){
            int p2=p+(d>0),q2=q+(d<0);
            if(p2==(m+1)/2&&q2==m/2){
                cout<<"Yes\n";
                for(i=0;i<m;i++)cout<<(i%2?-v:v)<<" ";
                cout<<"\n";
                return;
            }
            if(q2==(m+1)/2&&p2==m/2){
                cout<<"Yes\n";
                for(i=0;i<m;i++)cout<<(i%2?v:-v)<<" ";
                cout<<"\n";
                return;
            }
        }
        cout<<"No\n";return;
    }
    int t=-1;
    for(i=1;i<n-1;i++){
        if(a[i]*a[i]!=a[i-1]*a[i+1]){
            t=i;
            break;
        }
    }
    if(t==-1){
        if(a[1]&&a[0]*a[0]%a[1]==0&&go(0,a[0]*a[0]/a[1]))return;
        if(a[n-2]&&a[n-1]*a[n-1]%a[n-2]==0&&go(n,a[n-1]*a[n-1]/a[n-2]))return;
    }else{
        if(t>=2&&a[t-2]&&a[t-1]*a[t-1]%a[t-2]==0){
            if(go(t,a[t-1]*a[t-1]/a[t-2]))return;
        }
        if(t+1<n&&a[t+1]&&a[t]*a[t]%a[t+1]==0){
            if(go(t,a[t]*a[t]/a[t+1]))return;
        }
        if(t>=1&&a[t-1]&&a[t]*a[t]%a[t-1]==0){
            if(go(t+1,a[t]*a[t]/a[t-1]))return;
        }
        if(t+2<n&&a[t+2]&&a[t+1]*a[t+1]%a[t+2]==0){
            if(go(t+1,a[t+1]*a[t+1]/a[t+2]))return;
        }
    }
    cout<<"No\n";
}
signed main(){
    ios::sync_with_stdio(false);cin.tie(0);
    int _;cin>>_;
    while(_--)solve();
}

全部评论

(3) 回帖
加载中...
话题 回帖

等你来战

查看全部

热门推荐