上海计算机学会11月月赛 丙组题解
本次比赛涉及算法:数学、排列组合、高精度除法、思维、排序。本次丙组的比赛主要涉及数学比较多,还有一道高精度除法简化版。
比赛链接:https://iai.sh.cn/contest/57
标签:数学
题意:给定题目总量 n n n和每天完成的题目数量 m m m,求需要的天数。
题解:简单数学,不能整除的话多一天。
代码:
#include
using namespace std;
int main() {
int n, m;
cin >> n >> m;
if (n % m) cout << n / m + 1;
else cout << n / m;
return 0;
}
标签:数学、组合数学、乘法原理
题意:给定 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 4、7、2,染色方案: 2 ∗ ( 4 − 1 ) ∗ ( 7 − 2 ) = 30 2*(4-1)*(7-2)=30 2∗(4−1)∗(7−2)=30。我们先给序列从小到大排下序,排完序之后,比如样例: 2 、 4 、 7 2、4、7 2、4、7,第一个点有 2 2 2种染色选择( 1 、 2 1、2 1、2);第二个点正常可以有 4 4 4种染色方案,但是第一个点占用了一种颜色,染色选择减一;同理,对于第三个点来说,被前两个点占用了两种颜色,染色选择减二,依此类推,通过乘法原理 最终就能求解了。过程中,记得通过同余定理对模数取模。
代码:
#include
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;
}
标签:高精度除法
题意:给定一个正整数 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
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;
}
标签:数学、思维、排序
题意:给定 r r r行 c c c列的国际棋盘。棋盘上有 n n n只车,第 i i i只车在第 x i x_i xi行,第 y i y_i yi列,每只车能攻击同一行和同一列(包括自身),求这些车能够攻击的方格总数。
题解:洛谷 P3913 车的攻击(原题),数据比较大,直接模拟格子肯定不可行。我们先把车的坐标输入,分别排序,看看去重后 实际被攻击到的行数和列数有多少。如果正着直接用 实际被攻击的行数和实际被攻击到的列数乘积,发现会有重叠的部分,所以可以考虑换个思路,用总的格子数减去未被攻击的行数和列数的乘积(即不被攻击的格子数)。
代码:
#include
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;
}
标签:排序、数学、思维
题意:给定 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 k−1的位置,其他的同理,即对于第 i i i 个箱子来说,需要移动的距离为 a[k] - a[i] - (k-i)(i
代码:
#include
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;
}