牛客周赛Round13题解【ABCD】

写在前面:写题解只是督促自己补题,写的代码没有炫技成份

A-矩阵转置置

题目:https://ac.nowcoder.com/acm/contest/65507/A

通过题目给出的样例二可以发现,这道题的实质就是让我们对于输入的二维数组,从第N行到第1行,从第N列到第1列,先列后行打印输出。

代码:

#include <bits/stdc++.h>
using namespace std;

#define int long long
#define endl '\n'
const int N = 110;
int a[N][N];
signed main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= n; j ++)
            cin >> a[i][j];
    
    for (int i = n; i >= 1; i --)
    {
        for (int j = n; j >= 1; j --)
        {
            cout << a[i][j] << ' ';
        }
        cout << endl;
    }
    return 0;
}

B-小红买基金

题目:https://ac.nowcoder.com/acm/contest/65507/B

因为题目中给了买基金的限制,假如可以买的基金有res个,那么这道题的实质就是求C_{res}^1+C_{res}^2+...+C_{res}^{res},也可以看成是给你res个基金,每个基金有选和不选两种状态,那么pow(2,res)就是答案,只不过因为数据范围,所以要用到快速幂。下面给出1种快速幂,1种用逆元求组合数,1种公式推组合数写法。

快速幂代码

第33行减的0是减掉任何基金都不选的方案数

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int mod = 1e9 + 7;
int quick_pow(int a, int b)
{
    int res = 1;
    while (b)
    {
        if (b & 1)
        {
            res = res * a % mod;
        }
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}
signed main()
{
    cin.tie(0)->sync_with_stdio(0);
    
    int n, a, b;
    cin >> n >> a >> b;
    int cnt = 0;
    for (int i = 1; i <= n; i ++) 
    {
        int x, y;
        cin >> x >> y;
        if (x >= a && y <= b) cnt ++;
    }
    int res = quick_pow(2, cnt) - 1;
    cout << res << endl;
    return 0;
}

逆元求组合数板子

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int mod = 1e9 + 7;
const int N = 2e6 + 10;
int fact[N];//i的阶乘
int infact[N];//i的阶乘的逆元
int quick_pow(int a, int b)
{
    int res = 1;
    while (b)
    {
        if (b & 1)
        {
            res = res * a % mod;
        }
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}
signed main()
{
    cin.tie(0)->sync_with_stdio(0);
    fact[0] = infact[0] = 1;
    for (int i = 1; i < N; i ++)
    {
        fact[i] = i * fact[i - 1] % mod;
        infact[i] = infact[i - 1] * quick_pow(i, mod - 2) % mod;
    }
    
    int n, x, y;
    cin >> n >> x >> y;
    int res = 0;
    for (int i = 1; i <= n; i ++)
    {
        int a, b;
        cin >> a >> b;
        if (a >= x && b <= y) res ++;
    }
    int sum = 0;
    for (int i = 1; i <= res; i ++)
    {
        int k = fact[res] * infact[i] % mod * infact[res - i] % mod;
        sum = (sum + k) % mod;
    }
    cout << sum << endl;
    return 0;
}

公式推组合数代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int mod = 1e9 + 7;
int quick_pow(int a, int b)
{
    int res = 1;
    while (b)
    {
        if (b & 1)
        {
            res = res * a % mod;
        }
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}
signed main()
{
    cin.tie(0)->sync_with_stdio(0);
    int n, x, y;
    cin >> n >> x >> y;
    int res = 0;
    for (int i = 1; i <= n; i ++)
    {
        int a, b;
        cin >> a >> b;
        if (a >= x && b <= y) res ++;
    }
    int sum = 0;
    int last = 1;
    for (int i = 1; i <= res; i ++)
    {
        int now = last * (res - i + 1) % mod * quick_pow(i, mod - 2) % mod;//涉及到除法取模,除以i就是乘i的逆元,也就是quick_pow(i, mod - 2)
        last = now;
        sum = (sum + now) % mod;
    }
    cout << sum << endl;
    return 0;
}

C-小红的密码修改https://ac.nowcoder.com/acm/contest/65507/C

这道题我们分类讨论一下即可,因为一次只能修改一个字符,我们发现,当我们想修改的这一类字符(比如:数字这一类)的数量大于1个的时候,我们将其修改成任何字符都不会对合法性有影响。因为这一类的数量大于1个,所以总有剩下的字符是合法的,唯一注意的是,我们在修改的时候不能和没改之前一样。这4类字符一共有66个字符,我们以题意为例,数字有5个,所以数字能修改的方案就是5*66-5,其他类字符如此。

当某一类字符只出现一次的时候,比如样例中的a,那么它能修改的范围只有自己这一类,不然会不合法,那么a的修改方案就只有26-1=25种,其他亦如此。

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'

signed main()
{
    cin.tie(0)->sync_with_stdio(0);
    
    int t;
    cin >> t;
    while (t --)
    {
        string s;
        cin >> s;
        int a = 0, b = 0, c = 0, d = 0;
        
        for (int i = 0; i < s.size(); i ++)
        {
            if (s[i] == '.' || s[i] == ',' || s[i] == '!' || s[i] == '?') a ++;        
            else if (s[i] - '0' >= 0 && s[i] - '0' <= 9) b ++;
            else if (s[i] - 'A' >= 0 && s[i] - 'A' <= 25) c ++;
            else d ++;
        }
        int res = 0;
        if (a > 1) res += a * 66 - a;
        else res += 3;
        
        if (b > 1) res += b * 66 - b;
        else res += 9;
        
        if (c > 1) res += c * 66 - c;
        else res += 25;
        
        if (d > 1) res += d * 66 - d;
        else res += 25;
        
        cout << res << endl;
    }
    return 0;
}

D-小红的转账设置方式https://ac.nowcoder.com/acm/contest/65507/D

这道题第一个问题就是建个无向图,然后bfs求一下各个点到1号点的最短距离求和即可。对于第二个问题,咱们慢慢讨论。

首先:什么时候两个点之间的边可以任意方向?

答:情况一:两个点处于同一层,且有连边,如图:

2号点和3号点到1号点的最短距离都是1,连接2和3号点的这条边对两个点的最小值无影响,所以可以任意方向。

情况二:某个点到1号点的最短距离路径不止1条,如图点4。

只是张嘴说的话,这题就这两种情况,但是怎么用代码实现呢?

先看代码,代码中有注释:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N = 2e5 + 10;
const int mod = 1e9 + 7;

vector<int> g[N];//图
int n, m;
int dist[N];//各个点到1号点的最短距离
void bfs()//bfs求起点1到各个点的最短距离
{
    memset(dist, -1, sizeof dist);
    dist[1] = 0;
    queue<int> q;
    q.push(1);
    while (q.size())
    {
        auto p = q.front();
        q.pop();
        for (auto u : g[p])
        {
            if (dist[u] != -1) continue;
            dist[u] = dist[p] + 1;
            q.push(u);
        }
    }
}
int get_res1()//问题1,每个点的最短距离求和嘛
{
    int res = 0;
    for (int i = 1; i <= n; i ++) res = (dist[i] + res) % mod;
    return res;
}
signed main()
{
    cin.tie(0)->sync_with_stdio(0);
    
    cin >> n >> m;
    for (int i = 1; i <= m; i ++)//建无向图嘛
    {
        int a, b;
        cin >> a >> b;
        g[a].push_back(b);
        g[b].push_back(a);
    }
    bfs();
    int res1 = get_res1();//代价之和
    
    int res2 = 1;//第二问的答案
    int cnt1 = 0;//存两个点在同一层的边,后面的cnt1/2是因为无向边遍历的时候有重边
    for (int i = 2; i <= n; i ++)
    {
        int c = 0;
        for (auto u : g[i])
        {
            //我们说的第一种情况
            if (dist[u] == dist[i]) cnt1 ++;//同一层 两个点之间的边可以乱连 但是有重复的情况
            //第二种情况
            if (dist[u] == dist[i] - 1) c ++;//说明节点u有更近的路线到1 u和i之间的边可以乱连
        }
        int res = 1;
        for (int j = 1; j <= c; j ++) res = res * 2 % mod;//乘法原理
        res = (res - 1 + mod) % mod;//这里减1是因为图中的4号点,你不可能5和2都向4连边涩,那样4号点就出不去了
        
        res2 = res2 * res % mod;//乘法原理 总不可能是加吧
    }
    for (int i = 1; i <= cnt1 / 2; i ++)
    {
        res2 = res2 * 2 % mod;//乘法原理
    }
    cout << res1 << ' ' << res2 << endl;
    return 0;
}

E-小红打bosshttps://ac.nowcoder.com/acm/contest/65507/E

欧克,还没补,看其他题解去吧。。。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值