上海计算机学会11月月赛 丙组题解

上海计算机学会11月月赛 丙组题解

本次比赛涉及算法:数学、排列组合、高精度除法、思维、排序。本次丙组的比赛主要涉及数学比较多,还有一道高精度除法简化版。

比赛链接https://iai.sh.cn/contest/57

第一题:T1刷题

标签:数学
题意:给定题目总量 n n n和每天完成的题目数量 m m m,求需要的天数。
题解:简单数学,不能整除的话多一天。
代码

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

int main() {
    int n, m;
    cin >> n >> m;
    if (n % m) cout << n / m + 1;
    else cout << n / m;
    return 0;
}

第二题:T2染色

标签:数学、组合数学、乘法原理
题意:给定 n n n个点和每个点能染色的范围: 1 1 1 a i a_i ai之间的整数,求最终染色方案,答案对 1 0 9 + 7 10^9+7 109+7取模。
题解:比如样例中的 4 、 7 、 2 4、7、2 472,染色方案: 2 ∗ ( 4 − 1 ) ∗ ( 7 − 2 ) = 30 2*(4-1)*(7-2)=30 2(41)(72)=30。我们先给序列从小到大排下序,排完序之后,比如样例: 2 、 4 、 7 2、4、7 247,第一个点有 2 2 2种染色选择( 1 、 2 1、2 12);第二个点正常可以有 4 4 4种染色方案,但是第一个点占用了一种颜色,染色选择减一;同理,对于第三个点来说,被前两个点占用了两种颜色,染色选择减二,依此类推,通过乘法原理 最终就能求解了。过程中,记得通过同余定理对模数取模。
代码

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

typedef long long ll;
const ll mod = 1e9 + 7;
ll a[300005], ans = 1;

int main() {
    ll n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    sort(a + 1, a + 1 + n);

    for (int i = 1; i <= n; i++) {
        ans = ans * (a[i] - i + 1) % mod;
    }
    cout << ans << endl;
    return 0;
}

第三题:T3数根(二)

标签:高精度除法
题意:给定一个正整数 n n n,定义一个数字的根为它的十进制数字之和,例如 1234 1234 1234的数根为 1 + 2 + 3 + 4 1+2+3+4 1+2+3+4,请判定 n n n的数根能否整除 n n n。( 1 < = n < = 1 0 100000 1<=n<=10^{100000} 1<=n<=10100000
题解:高精度数以字符串形式输入,先求数根,最坏情况 99999 99999 99999位都是 9 9 9,显然数根是个低精度数。所以题目就转换成高精度数除于低精度数题目,最终看下取模的余数是否为 0 0 0,判断下能否整除即可。
代码

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

int main() {
    int m = 0, k = 0;
    string s;
    cin >> s;
    for (int i = 0; i < s.size(); i++) {
        m += (s[i] - '0');
    }

    for (int i = 0; i < s.size(); i++) {
        k = 10 * k + (s[i] - '0');
        k %= m;
    }

    if (k > 0) cout << "No";
    else cout << "Yes";
    return 0;
}

第四题:T4攻击的车

标签:数学、思维、排序
题意:给定 r r r c c c列的国际棋盘。棋盘上有 n n n只车,第 i i i只车在第 x i x_i xi行,第 y i y_i yi列,每只车能攻击同一行和同一列(包括自身),求这些车能够攻击的方格总数。
题解:洛谷 P3913 车的攻击(原题),数据比较大,直接模拟格子肯定不可行。我们先把车的坐标输入,分别排序,看看去重后 实际被攻击到的行数和列数有多少。如果正着直接用 实际被攻击的行数和实际被攻击到的列数乘积,发现会有重叠的部分,所以可以考虑换个思路,用总的格子数减去未被攻击的行数和列数的乘积(即不被攻击的格子数)。
代码

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

typedef long long ll;
ll x[1000005], y[1000005];

int main() {
    ll r, c, n;
    cin >> r >> c >> n;
    for (int i = 1; i <= n; i++) {
        cin >> x[i] >> y[i];
    }

    sort(x + 1, x + 1 + n);
    sort(y + 1, y + 1 + n);

    ll c1 = 1, c2 = 1;
    for (int i = 2; i <= n; i++) {
        if (x[i] != x[i - 1]) c1++;
        if (y[i] != y[i - 1]) c2++;
    }

    // 总的格子数减去未被攻击的格子数
    cout << r * c - (r - c1) * (c - c2);
    return 0;
}

第五题:T5推箱子

标签:排序、数学、思维
题意:给定 t t t组数据,每组数据给定长度为 n n n的字符串, @ @ @表示箱子, _ \_ _表示空格,求把箱子都推到一起(即两两箱子之间没有空格),最少移动次数。
题解:我们稍微把题意变下,给定 n n n个点在 x x x轴上的的坐标,求把这些点移到同一个位置的最少移动次数。
举个例子(坐标): 3   5   8   10 3 \ 5 \ 8 \ 10 3 5 8 10


如果把这些点都移到 2 2 2的坐标,移动次数为: 1 + 3 + 6 + 8 = 18 1+3+6+8=18 1+3+6+8=18
如果把这些点都移到 3 3 3的坐标,移动次数为: 0 + 2 + 5 + 7 = 14 0+2+5+7=14 0+2+5+7=14
如果把这些点都移到 5 5 5的坐标,移动次数为: 2 + 0 + 3 + 5 = 10 2+0+3+5=10 2+0+3+5=10
如果把这些点都移到 6 6 6的坐标,移动次数为: 3 + 1 + 2 + 4 = 10 3+1+2+4=10 3+1+2+4=10
如果把这些点都移到 8 8 8的坐标,移动次数为: 5 + 3 + 0 + 2 = 10 5+3+0+2=10 5+3+0+2=10
会发现把点移到这些点中最中间那个点,移动次数最少,推箱子问题同理,只不过得去考虑每个箱子会占一个位置,在中间位置( k k k)左边的第一个箱子推到 k − 1 k-1 k1的位置,其他的同理,即对于第 i i i 个箱子来说,需要移动的距离为 a[k] - a[i] - (k-i)(i<k);在中间位置右边的第 i i i个箱子,同理,需要移动的距离为 a[i] - a[k] - (i-k)(i>k)。

代码

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

typedef long long ll;
string s;
ll a[1000005];

int main() {
    ll t, n;
    cin >> t;
    while (t--) {
        cin >> n >> s;
        ll c = 0, ans = 0;
        for (int i = 0; i < n; i++) {
            if (s[i] == '@') a[++c] = i;
        }
        sort(a + 1, a + 1 + c);
        ll k = (c + 1) / 2;
        for (int i = 1; i <= c; i++) {
            if (i < k) ans += (a[k] - a[i] - (k - i));
            else ans += (a[i] - a[k] - (i - k));
        }
        cout << ans << endl;
    }
    return 0;
}
  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值