2019年安徽大学ACM/ICPC实验室新生赛题解

本文仅作个人收藏学习使用
题目及解析来源牛客竞赛网

//作者:王清楚
//链接:https://ac.nowcoder.com/discuss/351408?type=101&order=0&pos=7&page=1
//来源:牛客网

2019年安徽大学ACM/ICPC实验室新生赛(含题解)

2019-12-01 14:00:00 至 2019-12-01 18:00:00

                                                          2019年安徽大学ACM/ICPC实验室新生赛(公开赛)

A - 素数分布

题目描述

素数分布函数π(n)表示小于或等于n的素数的数目。例如π(10)=4(2,3,5,7是素数)。这个函数涉及到许多高等数论的内容,甚至和黎曼猜想挂钩,目前还有很多数学家正在不断探索其中的奥秘。千里之行始于足下,现在你开始关心一个问题:在正整数域中素数的分布是怎么样的。为了探索这个问题,你需要计算出一些π(n)的值。

输入描述:

第一行一个整数T(T≤1000),表示数据的组数。
接下来一共T行,第i+1(1≤i≤T)行表示第i组数据。每行一个正整数n(n≤1000)。

输出描述:

输出T行,第i行对应输入的第i组数据。每行一个正整数,表示 π(n)的值。

示例1
输入
 1
 10
输出
 4
题解

对于给定的 n(n≤1000),求出不超过 n 的素数的个数。
卡掉了最最暴力的做法,但是你只需要记录一个1000以内的素数表,然后每次在这个素数表里面统计就不会超时了。

AC代码
#include 
using namespace std;
int main() {
  vector<bool> isPrime(1001);
  isPrime[2] = true;
  for (int i = 3; i <= 1000; i++) {
    int sqt = sqrt(i);
    bool flag = true;
    for (int j = 2; flag && j <= sqt; j++)
      if (i % j == 0) flag = false;
    if (flag) isPrime[i] = true;
  }
  int T;
  cin >> T;
  while (T--) {
    int n;
    cin >> n;
    int ans = 0;
    for (int i = 2; i <= n; i++)
      if (isPrime[i]) ans++;
    cout << ans << endl;
  }
  return 0;
}

难度定位:codeforces rating 800


B - 食物分配

题目描述

众所周知,ICPC是一项团队赛事,需要三人合力协作完成。比赛的主办方会向参赛选手发放参赛服和食物,为了有备无患,准备的食物总是比参赛选手的总数要多一些。
假设你是一名ICPC教练,现在正带队参加一场ICPC区域赛。因为你们是最后一支注册的队伍而准备的食物还有剩余,因此你的队伍得到了4份食物(教练的食物是单独计算的)。因为工作人员的疏忽,每一份食物的分量不是均等的。如果恰好每人分到一份食物,那么没有人会抱怨。但现在多出了一份食物,所以你的队员希望每个人分到的食物分量是均等的。
一份食物只能被分给一个人,同时为了节约粮食,你不能浪费食物。如果没有方法把这四份食物分给三名队员并不使队员们产生抱怨,你只能将这份食物捐赠给慈善机构。
请判断是否存在一种方法可以将四份食物分给三名队员,每人获得的食物分量相等。

输入描述:

第一行一个整数T(1≤T≤100),表示数据的组数。
接下来一共T行,第i+1(1≤i≤T)行表示第i组数据,每行四个整数A,B,C,D (1≤A,B,C,D≤20000),分别表示四份食物的分量。

输出描述:

输出T行,第i行对应输入的第i组数据,每行一个整数。如果能将四份食物分给三个人且每人得到的食物分量相同,则输出每人得到的食物的分量。否则输出-1。

示例1
输入
 2
 1 2 3 3
 1 1 3 3
输出
 3
 -1
说明
 对于第一组数据,一名队员可以分到分量为1和分量为2的食物,另外两名队员则分到分量为3的食物。
 对于第二组数据,没有一种划分可以使得三名队员获得的食物分量相同。
题解

给定四个正整数,判断这四个正整数能否被恰好划分成三组,每一组数的和相等。
设这四个数分别为 A,B,C,D(A≤B≤C≤D),如果满足划分条件,则必然有 A+B=C 且 C=D,因此只需要对这四个数排序后判断即可。

AC代码
#include 
using namespace std;
int main() {
  int T;
  cin >> T;
  while (T--) {
    vector<int> nps(4);
    for (auto& i : nps) cin >> i;
    sort(nps.begin(), nps.end());
    if (nps[0] + nps[1] == nps[2] && nps[2] == nps[3])
      cout << nps[2] << endl;
    else
      cout << -1 << endl;
  }
  return 0;
}
}

难度定位:codeforces rating 1000


C - AHUICPC (Easy Version)

题目描述

本题的Easy Version与Hard Version的唯一区别是数据范围不同,因此可以通过Hard Version的代码可以直接通过Easy Version。
AHU ACM/ICPC实验室有着悠久而光荣的历史,学长学姐们拿过金牌,进过Google。无论经过多少人,我们都希望AHU ACM/ICPC实验室可以代表AHU的最高水平。正所谓与天壤而同久,共三光而永光。
现在给定一个整数n,表示AHU ACM/ICPC实验室经过了n(n≤10)代人。你需要让每个人得到一个字符串"AHUICPC",但由于n可能是一个很大的数,所以将字符串分给每一个人是不可行的。所以你现在要构造一个字符串,使得字符串中恰好有n个子序列为"AHUICPC",且字符串的长度不超过15。
对于字符串S,我们定义S[i1]S[i2] S[in]为S的长为n的子序列,其中i12< n 。当且仅当i1=j1,i2=j2, ,in=jn时S[i1]S[i2] S[in]和S[j1]S[j2] S[jn]被视作同一个子序列。
比如对于字符串"abbc",其中存在两个子序列为"abc",存在一个子序列为"ac",但是不存在子序列"cb"。

输入描述:

输入一个整数n(1≤n≤10),表示你需要构造的字符串中恰好含有n个子序列"AHUICPC"。

输出描述:

输出一行。一个长度不超过15的字符串。

示例1
输入
 1
输出
 AHUICPC
示例2
输入
 2
输出
 AHUIICPC
题解

询问只会有10种情况(1到10),其中只有10的情况下暴力构造的长度会超过15,对于这种情况只需要2个A和5个H就可以构造出10个子序列"AH",然后再后面补"UICPC"即可。

AC代码
#include 
using namespace std;
int main() {
  int n;
  cin >> n;
  if (n <= 9) {
    for (int i = 0; i < n; i++) cout << 'A';
    cout << "HUICPC\n";
  } else {
    cout << "AAHHHHHUICPC\n";
  }
  return 0;
}

难度定位:codeforces rating 800


D - 不定方程

题目描述

对于正整数a,b,我们有不定方程ax=by,其中x,y也是正整数。令c=ax=by,我们设cmin为该不定方程所有可能的的解中最小的一个。
给定a,b后求解cmin是一项简单的工作。现在我们把问题变得稍微复杂一些,我们给出x,y,你需要判断它们是否满足一组ax=by=cmin,其中cmin为该不定方程所有可能的的解中最小的一个。

输入描述:

第一行一个整数T(T≤100),表示数据的组数。
接下来一共T行,第i+1(1≤i≤T)行表示第i组数据。每一行两个整数x,y(1≤x,y≤105)。x,y的含义如题目描述所示。

输出描述:

输出T行,第i行对应输入的第i组数据。
如果满足ax=by=cmin,输出三个整数a,b,cmin,每个数之间存在空格。如果不满足则输出-1。
如果答案有多个,你可以输出其中任意一个,a,b,cmin不能超过263-1。

示例1
输入
 1
 3 5
输出
 5 3 15
说明
 5*3=3*5=15,可以证明如果a=5,b=3,那么对于ax=by=c,15是c可能的最小的取值。
 如果你输出10 6 30,那么这同样是一个正确的答案。因为有10*3=6*5=30,且同样30是可能的最小的取值。
题解

根据题意,cmin即为 a,b的最小公倍数 a⋅b/gcd(a,b),于是有x=b/gcd(a,b),y=a/gcd(a,b) 。可见 x,y 一定是互质的,即 gcd(x,y)=1。而此时只需要令 a=y,b=x 即可构造出答案。

注意因为 x,y 最大会到 105,所以两者之积有可能超过int的范围。

AC代码
#include 
using namespace std;
int main() {
  int T;
  cin >> T;
  while (T--) {
    long long x, y;
    cin >> x >> y;
    if (__gcd(x, y) != 1)
      cout << -1 << endl;
    else
      cout << y << ' ' << x << ' ' << x * y << endl;
  }
  return 0;
}

难度定位:codeforces rating 1400


E - 蕊蕊识数

题目描述

蕊蕊今年五岁了。她开始学习认识整数,因为一些数字非常大(足足有10100000 的规模 ! ),因此蕊蕊想通过一些方法给数字“瘦身”,具体是这样操作的:每次蕊蕊将当前数字n的所有十进制位的数加起来,得到一个新的数,这样重复若干次直到这个数只有一位。这个过程中会产生若干个数,蕊蕊认为这些数都代表同一个数n。为了证明这一点,蕊蕊运用了自己刚刚学的除法的概念,她希望找到另一个整数m,使得要么这若干个数(包括n本身)都能被m整除,要么这若干个数(包括n本身)都不能被m整除。显然对于不同的n,m是不同的。并且蕊蕊希望让m尽可能的小,但是m必须大于1。你能够帮蕊蕊找到这样的数m吗?

输入描述:

第一行一个整数T(T≤730),表示数据的组数。
接下来一共T行,第i+1(1≤i≤T)行表示第i组数据。每一个整数n (0≤n≤10100000)。
数据保证输入的字符总量小于3.5×106

输出描述:

输出T行,第i行对应输入的第i组数据。每行一个正整数,表示对应的n的最小的m值(m>1)。

示例1
输入
 2
 2
 64
输出
 2
 3
说明

64可以被2整除,64“瘦身”一次后得到的10也可以被2整除,但将64“瘦身”两次后得到1不可以被2整除。而64,10,1都不能被3整除。

题解

因为数据量极大,所以直接用python暴力计算是会超时的。
注意到对于任意的 10k (k≥0且为整数) ,其被3除的余数都是1,因此 a ⋅ 10k 和 a 对3同余。
任何十进制非负整数都可以表示成 ∑ai ⋅ 10k的多项式形式,其中 ai 恰好是该十进制数的各个数位。因此任意十进制非负整数被3除的余数,都等于将它的各个十进制位的数相加后被3除的余数。换而言之,3满足所有情况的 mm,而比3小的可能的答案只有2。
所以只需要判断一下2是否是答案,如果不是输出3即可。

AC代码
#include 
using namespace std;
int main() {
  int T;
  cin >> T;
  int _max = 0;
  while (T--) {
    string str;
    cin >> str;
    int num = 0;
    for (auto &i : str) num += i - '0';
    int check = (str.back() - '0') % 2;
    bool flag = true;
    while (num >= 10) {
      if (num % 2 != check) {
        flag = false;
        break;
      }
      str = to_string(num);
      num = 0;
      for (auto &i : str) num += i - '0';
    }
    if (flag && num % 2 == check)
      cout << 2 << endl;
    else
      cout << 3 << endl;
  }
  return 0;
}

python的AC代码

T = int(input())
while T:
    T -= 1
    n = input()
    check = int(n[-1]) % 2
    n = sum([int(i) for i in n])
    flag = True
    while n > 9:
        if n % 2 != check:
            flag = False
            break
        n = str(n)
        n = sum([int(i) for i in n])
    if flag and n % 2 == check:
        print(2)
    else:
        print(3)

难度定位:codeforces rating 1700


F - 蕊蕊乘车去上学

题目描述

蕊蕊今年五岁了,她是一个爱上学的好孩子,每天都要乘坐公交车去上学。可是今天在等车上学时,一个问题难倒了她,你能帮可爱的蕊蕊解决这个问题吗?
假设有一路公交,该路公交车每班车的发车间隔并不确定,该公交车的发车间隔有50%的概率是a分钟发出下一辆,有50%的概率是b分钟发出下一辆。每次车到站时都会接走所有的乘客。
现在蕊蕊到家楼下的车站坐车,假设公交车的行驶速度完全相同,且路上没有堵塞,又假设每分钟有一名乘客到达车站等车。那请问当蕊蕊上车时,乘客排队的平均队伍长度是多少?

输入描述:

输入的数据只包含一组,包括两个数a,b(10≤a,b≤100)。

输出描述:

输出一个数,乘客排队的平均长度,结果保留两位小数。

示例1
输入
 10 10
输出
 10.00
说明
 公交车一定是每10分钟到达一辆,到达时就会接走所有等车的乘客。
 那么公交车到达时一定会有10个人在等车,因为距离上一班公交车过去了10分钟,在这10分钟里来了10位乘客。
题解

本题可以先参考伯特兰悖论
题目所求即为等车的时间期望,我们设该期望为 pa ⋅ a + pb ⋅ b ,其中 pa 表示遇到发车间隔为 a 的公交车的概率,pb表示遇到发车间隔为 b 的公交车的概率。
注意到这个概率是时间上的概率,假设一辆公交车刚刚离开,那么距离下一辆公交车到达需要 t (t∈{a,b}) 分钟,而在这段时间内等车遇到的公交车的发车间隔都是 t。由于两种发车间隔出现的可能性是相等的,因此遇到的公交车发车间隔是 a 的概率为 a/(a+b),遇到的公交车发车间隔是 b 的概率为 b/(a+b)。最终的答案是 (a2+b2)/(a+b)。

AC代码
#include 
using namespace std;
int main() {
  int a, b;
  cin >> a >> b;
  cout << fixed << setprecision(2) << (double)(a * a + b * b) / (a + b) << endl;
  return 0;
}

难度定位:codeforces rating 1600


G - AHUICPC (Hard Version)

题目描述

本题的Easy Version与Hard Version的唯一区别是数据范围不同,因此可以通过Hard Version的代码可以直接通过Easy Version。
AHU ACM/ICPC实验室有着悠久而光荣的历史,学长学姐们拿过金牌,进过Google。无论经过多少人,我们都希望AHU ACM/ICPC实验室可以代表AHU的最高水平。正所谓与天壤而同久,共三光而永光。
现在给定一个整数n,表示AHU ACM/ICPC实验室经过了n(n≤109)代人。你需要让每个人得到一个字符串"AHUICPC",但由于n可能是一个很大的数,所以将字符串分给每一个人是不可行的。所以你现在要构造一个字符串,使得字符串中恰好有n个子序列为"AHUICPC",且字符串的长度不超过105
对于字符串S,我们定义S[i1]S[i2] S[in]为S的长为n的子序列,其中i12< n 。当且仅当i1=j1,i2=j2, ,in=jn时S[i1]S[i2] S[in]和S[j1]S[j2] S[jn]被视作同一个子序列。
比如对于字符串"abbc",其中存在两个子序列为"abc",存在一个子序列为"ac",但是不存在子序列"cb"。

输入描述:

输入一个整数n(1≤n≤109),表示你需要构造的字符串中恰好含有n个子序列"AHUICPC"。

输出描述:

输出一行。一个长度不超过105的字符串。

示例1
输入
 1
输出
 AHUICPC
示例2
输入
 2
输出
 AHUIICPC
题解

我们令 n=∑ai ⋅ bi ,且 bi ≤bi+1 ,如果我们构造出所有的恰好有 ai ⋅ bi个“AH”的字符串,并且它们之间互不影响,然后再在其后面接上"UICPC",那么就可以得到答案。且长度为 sqrt(n) 数量级。
事实上我们每一次都用 ai个A和 bi个H来构造,由于我们保证了 bi ≤ bi+1 ,所以我们可以把较小的 i 放在后面,把较大的 i 放在前面,这样我们可以重复利用较小的 i 的“H”。更形象地说,我们先取最后一项 ai ⋅ bi,此时会有 ai个A和 bi个H,接下来考虑前一项 ai−1 ⋅ bi−1,我们在恰好其后有 bi−1个H的位置插入 ai−1个A。如此不断重复。

AC代码
#include 
using namespace std;
vector<pair<int, int>> res;
int main() {
  int num;
  cin >> num;
  while (num > 0) {
    int tmp1 = sqrt(num), tmp2 = num / tmp1;
    while (tmp1 * tmp2 <= num) {
      tmp2++;
    }
    while (tmp1 * tmp2 > num) {
      tmp2--;
    }
    res.push_back(make_pair(tmp1, tmp2));
    num -= tmp1 * tmp2;
  }
  string str = "UICPC";
  res.push_back(make_pair(0, 0));
  for (int i = 0; i < res.size() - 1; i++) {
    for (int j = 0; j < res[i].second; j++) {
      str = "H" + str;
    }
    for (int j = 0; j < res[i].first - res[i + 1].first; j++) {
      str = "A" + str;
    }
  }
  cout << str << endl;
  return 0;
}

难度定位:codeforces rating 1900


H - 无尽大军

题目描述

恭喜你,勇士。如果你过关斩将来到了这一题面前,那么说明你具有非凡的才华。现在你将面临最终的考验,为了对抗最后也是最强大的敌人,你需要组建一支属于你自己的军队。
你将组建一支魔法军队。一开始只有一名士兵,但是每次你可以使用两种法术来扩大你的军队的规模。
A: 消耗2点法力值将你的军队规模翻倍,这意味着你的军队规模将会从k扩大至2k——增加了k名士兵。
B: 如果你之前使用过法术,那么你可以消耗1点法力值重现你上一次法术的效果。这意味着如果上一次使用法术时你的军队规模增加了k,那么你的军队会再增加k名士兵。
虽然你现在拥有无比强大的法力,但这并不意味着可以随便浪费——毕竟你不知道你最后的敌人究竟有多么强大。事实上你希望消耗最少的法力值就能让你的军队的规模达到n(n≤1014)。

输入描述:

输入一个整数n (1≤n≤1014)。

输出描述:

输出一个整数,表示你的军队规模达到n所需要消耗的最少法力值。

示例1
输入
 1
输出
0
示例2
输入
 6
输出
 5
说明
 一开始你的军队只有一名士兵,消耗2点法力值使你的军队规模翻倍。
 于是现在你有了两名士兵,再消耗1点法力值使军队达到3人。
 此时再消耗2点法力值就能达到6人。一共消耗5点法力值。
题解

抽象来说,对于起始的数字1,可以消耗2使其翻倍,即乘以2;而后既可以选择消耗2使其再乘以2,也可以消耗1使得上一次乘以 x 的操作变为乘以 x+1。
因此对于 n,我们的操作其实是构造一个等式 a1 ⋅ a2 ⋅ a3 ⋯ am = n,而操作的总消耗即等于 ∑ai
不难发现对于最小的总消耗,a1,a2,a3, ⋯ ,am恰好为 n 的全体质因数,因此将 n 质因数分解之后把所有因子相加即可得到答案。

AC代码
#include 
using namespace std;
vector<pair<int, int>> res;
int main() {
  long long n;
  cin >> n;
  long long sqt = sqrt(n);
  long long ans = 0;
  for (long long i = 2; i <= sqt; i++) {
    while (n % i == 0) {
      ans += i;
      n /= i;
    }
  }
  if (n != 1) ans += n;
  cout << ans << endl;
  return 0;
}

难度定位:codeforces rating 2000


你可能感兴趣的:(C/C++)