题解|2024牛客寒假04

原文链接:题解|2024牛客寒假04

A.柠檬可乐

1签

题目大意

第一行输入三个整数 a , b , k a,b,k a,b,k,判断是否 a ≥ k × b a\ge k\times b ak×b

解题思路

如题

参考程序

void solve()
{
    ll a,b,k;
    cin >> a >> b >> k;
    if(a>=b*k) cout << "good\n";
    else cout << "bad\n";
}

B.左右互博

思维

题目大意

n n n堆石子,每堆石子有 a i a_i ai个,两个人轮流取操作。
每次操作可以选择一堆至少有 2 2 2个石子的石子堆,然后任意分成两堆,每堆至少有一个石子。
最先无法操作的人输。

解题思路

对于有 x x x个石子的石堆,它被分成 x x x堆一个石子的石堆,需要操作 x − 1 x-1 x1次。
因此,对所有石堆的操作次数求和 s u m = ∑ i = 1 n a i − 1 sum = \sum\limits_{i=1}^{n}a_i-1 sum=i=1nai1
判断奇偶性

参考程序

void solve()
{
    ll n,t;cin >> n;
    ll ans=0;
    FORLL(i,1,n){
        cin >> t;
        ans+=t-1;
    }
    if(ans%2) cout << "gui" << endl;
    else cout << "sweet" << endl;
}

C.冬眠

模拟

题目大意

给定 n × m n\times m n×m的字符矩阵和一段有 q q q次操作的操作序列,包含两种操作:

  1. 1 t表示将第 t t t行向右移动 1 1 1次,最右的一个字符移到最左的位置。
  2. 2 t表示将第 t t t列向下移动 1 1 1次,最下的一个字符移到最上的位置。
    问最后的字符矩阵第 x x x行第 y y y列的字符是什么

解题思路

数据范围不大,模拟操作

参考程序

void solve()
{
    ll n,m,x,y;cin >> n >> m >> x >> y;
    x--;y--;
    vector<string> mp(n);
    FORLL(i,0,n-1) cin >> mp[i];
    ll p,q;cin >> p >> q;
    vector<pll> op(q);
    FORLL(i,0,q-1) cin >> op[i].first >> op[i].second;
    char tc;
    while(p--){
        FORLL(i,0,q-1){
            if(op[i].first==1){//行向右循环移动一次
                tc=mp[op[i].second-1][m-1];
                FORLL_rev(j,m-1,1) mp[op[i].second-1][j]=mp[op[i].second-1][j-1];
                mp[op[i].second-1][0]=tc;
            }else{//列向下循环移动一次
                tc=mp[n-1][op[i].second-1];
                FORLL_rev(j,n-1,1) mp[j][op[i].second-1]=mp[j-1][op[i].second-1];
                mp[0][op[i].second-1]=tc;
            }
        }
    }cout << mp[x][y] << endl;
}

D.守恒

数学

题目大意

给定 n n n个正整数,可以将它们的和重新分配为 n n n个正整数。
求所有分配方法中,这 n n n个数的最大公约数有多少种可能性。

解题思路

先对所有数求和,记为 s u m sum sum
假设这 n n n个数的最大公约数为 g g g,则 g g g一定是 s u m sum sum的因子。
且因为这 n n n个数至少都为 g g g,所以 s u m / g ≥ n sum/g\ge n sum/gn

O ( ( n ) ) O(\sqrt(n)) O(( n))遍历 s u m sum sum的因子,判断是否满足条件

注意特判:当 n = 1 n=1 n=1时,答案为 1 1 1

参考程序

void solve()
{
    ll n;cin >> n;
    ll sum=0,t;
    FORLL(i,1,n) {cin >> t;sum+=t;}
    if(n==1) {cout << 1 << endl;return;}//特判
    ll ub=sqrt(sum)+1;
    ll ans=0;
    FORLL(i,1,ub){
        if(i*i>sum) break;
        if(sum%i==0){
            t=sum/i;
            if(t==i){
                if(t>=n) ans++;
            }else{
                if(t>=n) ans++;
                if(i>=n) ans++;
            }
        }
    }cout << ans << endl;
}

E.漂亮数组

思维

题目大意

给定一个长度为 n n n的整数数组 a a a和一个正整数 k k k
可以将 a a a划分成任意个非空子串,如果子串和能被 k k k整除,则称这个子串是漂亮的。
求 使得漂亮子串最多的方案 得到的漂亮子串的个数。

解题思路

从前往后遍历,计算(从上个割点开始的)前缀和对 k k k的余数 p r e pre pre
如果当前位置的前缀和余数 p r e i pre_i prei在上个割点之后的位置 j j j有出现过,即 p r e i = p r e j pre_i=pre_j prei=prej,则子串 [ j + 1 , i ] [j+1,i] [j+1,i]区间和能被 k k k整除。

因此,我们可以记录每个前缀和对 k k k的余数在上个割点之后是否出现过,贪心的求出最多的漂亮子串个数。

时间复杂度: O ( n ) O(n) O(n)

参考程序

void solve()
{
    ll n,k,t;cin >> n >> k;
    map<ll,int> mp;
    mp[0]=1;//余数为0即空
    ll sum=0,ans=0;
    FORLL(i,1,n){
        cin >> t;
        sum+=t;
        if(mp[sum%k]){//如果当前 前缀和对k的余数在上个割点之后出现过
            ans++;
            sum=0;
            mp.clear();//清空map
            mp[0]=1;
        }else{
            mp[sum%k]=1;
        }
    }cout << ans << endl;
}

G.数三角形(easy)

计数

题目大意

*看作实体,.看作空白,等腰三角形具有类似以下的形状且不可旋转:

一阶:
.*.
*** 

二阶:
..*..
.*.*.
*****

三阶:
...*...
..*.*..
.*...*.
*******

以此类推,给定一个 n × m n\times m n×m的字符矩阵,求有多少个等腰三角形

解题思路

枚举三角形的顶点,然后向两边延展,判断是否满足条件。

对于底边,如果每次都从左往右扫描,则时间复杂度较高。
可以预处理每一个*在该行连续最左的*的位置,使用时直接查询 O ( 1 ) O(1) O(1)判断。

参考程序

void solve()
{
    ll n,m;
    char mp[N][N]={0};//字符矩阵
    cin >> n >> m;
    FORLL(i,1,n) FORLL(j,1,m) cin >> mp[i][j];
    ll pre[N][N]={0};
    FORLL(i,1,n){//预处理
        FORLL(j,1,m){
            if(mp[i][j]=='*'){
                if(pre[i][j-1]) pre[i][j]=pre[i][j-1];//如果当前位置是*,且左边有*,则pre[i][j]等于左边的*的位置
                else pre[i][j]=j;//否则pre[i][j]等于当前位置
            }
            else pre[i][j]=0;
        }
    }
    // FORLL(i,1,n) FORLL(j,1,m) cout << pre[i][j] << " \n"[j==m];//test
    ll ans=0,t;
    FORLL(i,1,n) FORLL(j,1,m) if(mp[i][j]=='*'){
        ll l=j-1,r=j+1,d=i+1;
        //d表示当前底边所在的行,l和r表示底边的两个端点位置
        while(l>=1&&r<=m&&d<=n&&mp[d][l]=='*'&&mp[d][r]=='*'){
            t=0;
            if(pre[d][r]<=l) t=1;
            if(t) ans++;
            l--;r++;d++;
        }
    }cout << ans << endl;
}
  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深翼CCLMSY

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值