牛客小白月赛101

题目链接

A

k 次操作,每次删除数组的第一个元素或者最后一个元素,求最后数组和的最大值

错误做法:每次操作比较第一个元素和最后一个元素,删除较小的一个。

这样不能只能保证一次操作是最优的;对于多次删除操作,不能保证

正确做法:维护一个 n - k 长度的滑动窗口,遍历整个数组求得滑动窗口和的最大值

代码

cpp 复制代码
void slove()
{
    int n, k; cin >> n >> k;
    vector<int> a(n);
    for (int i = 0; i < n; i++) cin >> a[i];
    
    LL res = 0;
    for (int i = 0; i < n - k; i++)
    {
        res += a[i];
    }
    LL t = res;
    for (int i = n - k; i < n; i++)
    {
        t -= a[i - n + k];
        t += a[i];
        res = max(res, t);
    }
    cout << res << endl;
}  

B

和括号匹配是一样的,使用一个栈模拟。

代码

cpp 复制代码
int main()
{
    int n; cin >> n;
    string s; cin >> s;
    string stk;
    for (int i = 0; i < s.size(); i++)
    {
        stk.push_back(s[i]);
        if (stk.size() >= 2)
        {
            string t;
            t += stk[stk.size() - 2];
            t += stk[stk.size() - 1];
            if (t == "fc" || t == "tb")
            {
                stk.pop_back();
                stk.pop_back();
            }
        }
    }
    cout << stk.size() << endl;
}

C

先明白一个结论:当 n 为偶数,gcd(n, n - 2) = 2(n > 2)

从(1, 1)到(2, 2)需要 2 步(对于任意整数 n,gcd(n, n) = n)

所以当 n 为偶数,最少步数为 4

当 n 为奇数时,gcd(n - 1, n - 3) = 2 (n > 3)。n - 1 为偶数,从(1,1)到(n - 1, n - 1)最少步数为 4,(n - 1,n - 1)到(n,n)需要 2 步

所以当 n 为奇数,最少步数为 6

代码

cpp 复制代码
void slove()
{
    int n; cin >> n;
    int res = 2 * (n - 1);
    if (n & 1)
        res = min(res, 6);
    else
        res = min(res, 4);
    cout << res << endl;
}

D

每次询问给定一个 x ,需要输出给出包含了 x 位置且区间和为完全平方数的连续子数组个数。

题意转换就是:包含 x 位置且区间和为完全平方数的区间有多少个

利用前缀和预处理好数组的每一个区间(数组长度:n <= 1000);当一个区间和为完全平方数时,使用差分改变区间

代码

cpp 复制代码
const int N = 1010;
int a[N], pre[N];
int n, q;
int d[N];
void slove()
{
    cin >> n >> q;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        pre[i] = pre[i - 1] + a[i];
    }
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            int u = pre[j] - pre[i - 1];
            int x = sqrt(u);
            if (x * x != u) continue;
            d[i] ++;
            d[j + 1] --;
        }
    }
    for (int i = 1; i <= n; i++) d[i] = d[i - 1] + d[i];
    
    while (q--)
    {
        int x; cin >> x;
        cout << d[x] << endl;
    }
}  

E

题意转换:对于数组中的任意一个元素 a,令 a 的约数个数为 x,数组为 a 的约数个数为 y,求有多少个元素,使得 x = y。就是说数组中一个元素的约数包含它本身的所有约数

先对数组排序,会有重复的元素,可以先去重,也可以一起处理。

遍历数组,使用数组 cnt,cnt[j] 表示 j 在数组中的约数个数

取元素的上界 N,在记录一遍约数个数

最后 cnt[i] = 0,表示 i 是一个满足要求的元素

cpp 复制代码
const int N = 1e6 + 10;
int nums[N], cnt[N];
int n;

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> nums[i];
    
    sort(nums + 1, nums + 1 + n);
    
    for (int i = 1; i <= n; i++)
    {
        if (nums[i] != nums[i - 1])
        {
            int j = nums[i];
            while (j < N)
            {
                cnt[j]++;
                j += nums[i];
            }
        }
    }
    
    for (int i = 1; i < N; i++)
    {
        int j = i;
        while (j < N)
        {
            cnt[j]--;
            j += i;
        }
    }
    int res = 0;
    for (int i = 1; i < N; i++)
        if (cnt[i] == 0) res++;
        
    cout << res << endl;
}

F

对数组 A 操作 n 次,每次在区间 [i, i + w] 中选择一个数和 A[i] 交换,最后变为数组 B。

遍历数组 B,当前元素为 B[i],可以有两种情况:方案数 cnt

  1. B[i] 在 A 中:可以将 A 分为三个区间 [1, i),[i, i + w],(i + w, n] 。
    假如在第一个区间:cnt *= 1,第二个区间:cnt *= 1, 第三个区间:cnt = 0
  2. B[i] 不在 A 中:在区间 [1, i + w] 中还有 x 个 -1 没有被使用:cnt *= x

当前遍历到 B[i],那么意味着 B[i] 之前的元素已经被确定了,如果在区间 [1, i) 中有 B[i],表示在 i 次操作之前将该元素换到后面了。

如果在(i + w, n] ,表示不可能通过交换到当前位置(操作是从左至右遍历的)

cpp 复制代码
#include <iostream>
#include <cstring>
using namespace std;
using LL = long long;
const int N = 2e5 + 10, MOD = 998244353;
int n, w;
int a[N], b[N], pos[N];
LL pre[N];
void solve()
{
    cin >> n >> w;
    memset(pos, 0, sizeof pos);  // pos 数组记录 a 中元素的下标
    memset(pre, 0, sizeof pre);  // pre 数组记录当前位置之前有多少个 -1
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        if (a[i] != -1)
            pos[a[i]] = i;
        if (a[i] == -1)
            pre[i] = 1;
    }
    for (int i = 1; i <= n; i++) pre[i] = pre[i - 1] + pre[i];
    for (int i = 1; i <= n; i++) cin >> b[i];
    
    int res = 1, cnt = 0;  // cnt 表示当前已经有多少个 -1 被使用了
    for (int i = 1; i <= n; i++)
    {
        if (pos[b[i]] != 0)
        {
            if (pos[b[i]] > i + w)
                res = 0;
        }
        else
        {
            int x = pre[min(i + w, n)] - cnt;
            res = (LL) res * x % MOD;
            cnt++;
        }
    }
    cout << res << endl;
}

int main()
{
    int t; cin >> t;
    while (t--) solve();
    return 0;
}
相关推荐
JSU_曾是此间年少几秒前
数据结构——线性表与链表
数据结构·c++·算法
此生只爱蛋1 小时前
【手撕排序2】快速排序
c语言·c++·算法·排序算法
咕咕吖1 小时前
对称二叉树(力扣101)
算法·leetcode·职场和发展
九圣残炎2 小时前
【从零开始的LeetCode-算法】1456. 定长子串中元音的最大数目
java·算法·leetcode
lulu_gh_yu2 小时前
数据结构之排序补充
c语言·开发语言·数据结构·c++·学习·算法·排序算法
丫头,冲鸭!!!2 小时前
B树(B-Tree)和B+树(B+ Tree)
笔记·算法
Re.不晚2 小时前
Java入门15——抽象类
java·开发语言·学习·算法·intellij-idea
为什么这亚子3 小时前
九、Go语言快速入门之map
运维·开发语言·后端·算法·云原生·golang·云计算
4 小时前
开源竞争-数据驱动成长-11/05-大专生的思考
人工智能·笔记·学习·算法·机器学习
~yY…s<#>4 小时前
【刷题17】最小栈、栈的压入弹出、逆波兰表达式
c语言·数据结构·c++·算法·leetcode