牛客小白月赛93

题解与反思

反思:做前3个题就很困难,对小白一个不友好,看到几题都是大数的运算,以为全是高精度,还特意去学习了一下,结果高精度乘法是o(n^2)的用不了,这时候才考虑其他的做法,卡了好久。

B 交换数字

贪心的部分一开始就猜到了,就是后边的乘法卡了好久

  1. 首先我们要考虑怎么交换能够使a * b 最小,猜测一下:a用最小的数字,b用最大的数字,然后用样例测试了一下发现是正确的。

  2. 官方题解给出了证明:太妙了

由于我们交换任意位置的数字,a+b 的值恒为定值,只会改变 a−b 的值,而我们知道 4ab=(a+b) * (a+b) − (a−b) * (a-b),于是我们最大化 a−b 的值即可最小化 ab 的值。

  1. 最后一点如何求a * b 的值:我们先求出来a的值,因为最后要取模,所以在求a的值的时候每一步都取模就可以了,b同理。

  2. a的值的具体求法:

 for(int i = 0; i < n; i ++)
    {
        LL x = a[i] - '0';
        aa = aa * 10 + x;
        aa %= mod;
    }
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int mod = 998244353;
int main()
{

    int n;
    string a, b;
    cin >> n >> a >> b;
    for(int i = 0; i < n; i ++)
    {
        if(a[i] > b[i]) swap(a[i], b[i]);
    }
    //cout << a << endl << b;
    LL aa = 0, bb = 0;
    for(int i = 0; i < n; i ++)
    {
        LL x = a[i] - '0';
        //cout << x << endl;
        aa = aa * 10 + x;
        aa %= mod;
    }
    for(int i = 0; i < n; i ++)
    {
        LL x = b[i] - '0';
        //cout << x << endl;
        bb = bb * 10 + x;
        bb %= mod;
    }
    LL res = aa % mod * bb % mod % mod;
    cout << res << endl;
    return 0;
}

C 老虎机

求期望,高中数学问题,好久没做了,有点不习惯卡住了。看到最后答案的输出结果一眼快速幂

  1. 根据题意分三种情况讨论,然后分子分母分开算即可。

  2. 分类讨论:n >= 3 时

(1)没有相同的图案:C(n,3) * A(3,3) , 选出三的图案进行全排列

(2)两个相同的图案:

C(n,2) * (A(3,3) / A(2,2)) * C(2,1) = C(n,2) * A(3,3), 选出两个图案之后,我们进行全排列,因为有两个相同的图案我们要除以顺序即A(2,2), 最后我们还要选择这两个图案哪一个出现两次即C(2,1)

(3)三个相同的图案:C(n,1)

(4)总的方案数:n * n * n,每个位置上n个图案都可能出现

(5)n = 1 和 n = 2 的情况很容易得出

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int mod = 998244353;
LL qmi(LL a, LL k, LL p)
{
    LL res = 1;
    res = res % p;
    while(k)
    {
        if(k & 1) res = res * a % p;
        a = a * a % p;
        k >>= 1;
    }
    return res;
}
int main()
{

    int t;
    cin >> t;
    while(t --)
    {
        LL n, a, b, c;
        cin >> n >> a >> b >> c;
        if(n >= 3)
        {
            LL res_n = (n - 1) * (n - 2) % mod * a % mod + 3 * (n - 1) * b % mod + c;
            res_n %= mod;
            LL res_m = n * n % mod;
            LL res = res_n * qmi(res_m, mod - 2, mod) % mod;
            cout << res << endl;
        }
        else if(n == 2)
        {
            LL res_n = 6 * b + 2 * c;
            res_n %= mod;
            LL res_m = n * n * n % mod;
            //cout << res_n << endl << res_m << endl;
            LL res = res_n * qmi(res_m, mod - 2, mod) % mod;
            cout << res << endl;
        }
        else
        {
            LL res = c;
            cout << res << endl;
        }
    }
    return 0;
}

D 幻兽帕鲁

不会模拟过程,看了题解是打表找规律。还有一点没注意到的是重新分组之后是根据下标重新判断奇,偶。

  1. 模拟一下这个过程:
void dfs(int l, int r)
{
    if(l == r) return ;
    int mid = (l + r) / 2;
    vector<int> ou, ji;
    for(int i = l; i <= r; i ++)
    {
        if((i - l) % 2 == 0) ou.push_back(a[i]);
        else ji.push_back(a[i]);
    }
    //for(auto it : ou) cout << it  << ' ';
    //cout << endl;
    for(int i = l, k = 0; i <= mid; i ++)
    {
        a[i] = ou[k++];
    }
    for(int i = mid + 1, k = 0; i <= r; i ++)
    {
        a[i] = ji[k++];
    }
    dfs(l, mid);
    dfs(mid + 1, r);
 
}
  1. 打表发现规律,x二进制展开,之后变成逆序的二进制即为答案。例如3 --> 011 <--> 110 --> 6
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 998244353;
const int N = 2e5 + 10;
int a[N];
void dfs(int l, int r)
{
    if(l == r) return ;
    int mid = (l + r) / 2;
    vector<int> ou, ji;
    for(int i = l; i <= r; i ++)
    {
        if((i - l) % 2 == 0) ou.push_back(a[i]);
        else ji.push_back(a[i]);
    }
    //for(auto it : ou) cout << it  << ' ';
    //cout << endl;
    for(int i = l, k = 0; i <= mid; i ++)
    {
        a[i] = ou[k++];
    }
    for(int i = mid + 1, k = 0; i <= r; i ++)
    {
        a[i] = ji[k++];
    }
    dfs(l, mid);
    dfs(mid + 1, r);

}
int main()
{
    int n, m;
    cin >> n >> m;
    while(m --)
    {
        LL x, res = 0, k = n - 1;
        cin >> x;
        while(x)
        {
           if(x & 1)
           {
               res += (LL)pow(2, k);
           }
           k --;
           x >>= 1;
        }
        cout << res << endl;
    }
    return 0;
}

E 奏绝

实现起来倒是不麻烦,但是能够推出来这个结果还是挺难的

1. 有一点需要注意一下,在取模的结果中经常出现,就是计算过程中有减法出现,这时候我们应该先加上mod,防止变成负数,再取模。res = (res % mod + mod) % mod;

  1. 显然在计算m次答案时无法进行遍历,所以要预处理一些东西。

用a0[i]和a1[i]表示从1到i中0的个数和1的个数,b0[i]和b1[i]表示从1到i中0的坐标总和与1的坐标总和,sum[i]表示1到i总影响值

更新上述列表是简单的,遍历一边序列即可全部更新完成。

最后是计算答案,对于每一个l, r,答案首先为ans[r] - ans[l - 1],不过l前面的数仍然对剩余的答案有影响,所以要减去l前面的0与l至r中的1的坐标的组合,以及l前面的1与l~r中的0的坐标的组合。

直接看代码吧:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 998244353;
const int N = 2e5 + 10;
// a: 1 or 0 的个数和
// b: i or 0 的坐标和
LL sum[N], a0[N], a1[N], b0[N], b1[N];
int main()
{
    int n, m;
    cin >> n >> m;
    string s;
    cin >> s;
    s = " " + s;
    for(int i = 1; i <= n; i ++)
    {
        a0[i] = a0[i - 1];
        a1[i] = a1[i - 1];
        b0[i] = b0[i - 1];
        b1[i] = b1[i - 1];
        if(s[i] == '0')
        {
            a0[i] ++;
            b0[i] += i;
        }
        else
        {
            a1[i] ++;
            b1[i] += i;
        }
    }
    for(int i = 1; i <= n; i ++)
    {
        //sum[i] = sum[i - 1];
        if(s[i] == '0')
        {
            sum[i] = sum[i - 1] + a1[i - 1] * i - b1[i - 1];
        }
        else
        {
            sum[i] = sum[i - 1] +  a0[i - 1] * i - b0[i - 1];
        }
    }
    while(m --)
    {
        int l, r;
        cin >> l >> r;
        LL res = (sum[r] - sum[l - 1]) % mod - (a0[l - 1] * (b1[r] - b1[l - 1]) - (a1[r] - a1[l - 1]) * b0[l - 1]) % mod
                                      - (a1[l - 1] * (b0[r] - b0[l - 1]) - (a0[r] - a0[l - 1]) * b1[l - 1]) % mod;
        res = (res % mod + mod) % mod;
        cout << res << endl;
    }
    return 0;
}

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值