Educational Codeforces Round 104 (Rated for Div. 2)
A. Arena
题意
n个英雄互相随机切磋,获胜后英雄等级提升一级,平局则无胜者,已知n位英雄的初始英雄等级,问进行 10 0 500 100^{500} 100500次比赛后可能的胜者有几个?
思路
签到题,显然等级最低的英雄无论如何都无法升级,而其他英雄可以通过攻打低级英雄升级。所以除了等级最低的英雄以外,都可能成为胜者。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int T, n, a[105];
int main() {
cin >> T;
while(T--) {
cin >> n;
int minn = 105;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
minn = min(minn, a[i]);
}
int cnt = 0;
for (int i = 1; i <= n; ++i) {
if (a[i] == minn) cnt++;
}
cout << n - cnt << '\n';
}
return 0;
}
B. Cat Cycle
题意
家中有n个猫咪休息处,猫咪A会依次在编号n,n-1,…,2,1,n,n-1,…的休息处循环休息,猫咪B会依次在编号1,2,…,n-1,n,1,2,…的休息处循环休息。
当B即将休息的位置和A冲突时,它会跳过这个位置到下一处休息。问,已知n个位置和时刻k,猫咪B在哪里休息?
思路
简单模拟可得,当n为偶数时,两只猫休息不会发生冲突,故只需计算循环了几轮n即可;当n为奇数时,每过n/2轮就会发生一次冲突。所以位置和偶数的计算公式偏移了k/(n/2)个位子。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
int T;
ll n, k;
int main() {
cin >> T;
while(T--) {
cin >> n >> k;
ll ans = (k - 1) % n + 1; //保证k整除n时答案为n而不是0
if (n % 2) ans = (ans + (k - 1) / (n / 2) - 1) % n + 1;
cout << ans << '\n';
}
return 0;
}
C. Minimum Ties
题意
n支足球队两两比赛,获胜队得3分,失败队得0分,平局队得1分。要求构造一种比赛情况,使得比赛结束后所有球队得分相同,且使平局最少。
思路
每支球队要进行n-1场比赛,且一场比赛有胜必有负,所以要想让分数完全一致,所有球队的胜、负、平场次应该相同。
①当n为奇数,则n-1为偶数,那么让所有球队获胜(n-1)/2场,失败(n-1)/2场就行。构造球队i比赛情况时,对于编号大于i的队伍,按一胜一负的顺序循环比即可。
②当n为偶数,则n-1为奇数。设平局x场,则:
令队伍分数和 s u m = 3 ∗ ( n ( n − 1 ) 2 − x ) + 2 x sum = 3*(\frac{n(n-1)}{2} - x)+2x sum=3∗(2n(n−1)−x)+2x,则每个球队得分 s u m / n sum/n sum/n,可证有整数解时, x x x至少取 n / 2 n/2 n/2。即每队刚好一次平局。
可以这样构造:
则球队i(编号1~n)为奇数时,向后循环0,1,-1,1,-1……构造,为偶数时,向后循环-1,1,-1,1……构造。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
int T, n, a[105][105];
string s;
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> T;
while(T--) {
memset(a, 0, sizeof(a));
cin >> n;
if (n % 2) {
for (int i = 1; i < n; ++i) {
a[i][i + 1] = 1;
for (int j = i + 2; j <= n; ++j) {
a[i][j] = -a[i][j - 1];
}
}
}
else {
for (int i = 1; i < n - 1; ++i) {
a[i][i + i % 2 + 1] = -1 + (i % 2) * 2;
//i为奇数时,跳过a[i][i+1],即默认该局为平局
for (int j = i + i % 2 + 2; j <= n; ++j) {
a[i][j] = -a[i][j - 1];
}
}
}
for (int i = 1; i < n; ++i) {
for (int j = i + 1; j <= n; ++j) {
cout << a[i][j] << ' ';
}
}
cout << '\n';
}
return 0;
}
D. Pythagorean Triples
题意
已知n,求满足 1 < = a < = b < = c < = n 1 <= a <= b <= c <= n 1<=a<=b<=c<=n、能构成直角三角形、满足 c = a 2 − b c = a^2 - b c=a2−b 的整数三元组(a, b, c)的个数。
思路
易得两等式:
c 2 = a 2 + b 2 c^2 = a^2 + b^2 c2=a2+b2 ………… ①
c = a 2 − b c = a^2 - b c=a2−b ………… ②
① - ②, 得: c 2 − c = b 2 + b c^2 - c = b^2 + b c2−c=b2+b
即 c ∗ ( c − 1 ) = b ∗ ( b + 1 ) c * (c-1) = b*(b+1) c∗(c−1)=b∗(b+1)
∴ b = c − 1 b = c - 1 b=c−1 …………③
将③代入①,得 a 2 = 2 c − 1 a^2=2c-1 a2=2c−1
即 c = a 2 + 1 2 c = \frac{a^2+1}{2} c=2a2+1 …………④
由于 c c c是整数,所以右式需符合整除条件,即 a a a为奇数。至少有两个数小于 c c c,故从3开始枚举奇数作为 a a a,利用 c = a 2 + 1 2 ∈ [ 3 , n ] c = \frac{a^2+1}{2}∈[3,n] c=2a2+1∈[3,n]的范围要求求解。
n最大为1e9,每组样例时间复杂度在 2 e 9 \sqrt{2e9} 2e9 以内,暴力计数即可。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
int T;
ll n;
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> T;
while(T--) {
cin >> n;
int cnt = 0;
for (ll i = 3; (i * i + 1) / 2 <= n; i += 2) cnt++;
cout << cnt << '\n';
}
return 0;
}
E. Cheap Dinner
题意
合格的晚餐由四道菜组成,分别是第一道菜、第二道菜、饮品、甜品。每道菜分别有 a i 、 b i 、 c i 、 d i a_i、b_i、c_i、d_i ai、bi、ci、di种菜品可以选择,且知道它们分别的价格。相邻两种菜可能存在互斥关系,即不能同时上桌。给出四道菜分别有多少菜品可选以及它们的价格、互斥关系,求最少用多少钱可以构出一桌晚餐。
思路
显然,菜品之间存在转换关系。考虑状态转移:
对于第i道菜的第j种菜品,选择前i-1道菜合法的、花费最小的方案。
暴力的思路就是对于 d p [ i ] [ j ] dp[i][j] dp[i][j],遍历 d p [ i − 1 ] [ k ] dp[i-1][k] dp[i−1][k],找出 k 、 j k、j k、j不互斥中 d p [ i − 1 ] [ k ] dp[i-1][k] dp[i−1][k]最小的方案,
则 d p [ i ] [ j ] = d p [ i − 1 ] [ k ] + p r i c e [ i ] [ j ] dp[i][j]=dp[i-1][k]+price[i][j] dp[i][j]=dp[i−1][k]+price[i][j]
显然,时间复杂度不允许。那么,我们的问题转变为:如何快速找到我们所需要的k?
map可以解决这个问题。首先,map自带排序,我们只需要在进入第 i i i道菜时,在遍历该工序每种菜之前预处理一个map计数数组,把上一层工序的所有所需花费 d p [ i − 1 ] [ j ] dp[i-1][j] dp[i−1][j]存下来;然后,对于当前菜,删除它所有的互斥边,此时map中剩余的第一个元素就是满足的k。
最后,我们在 d p [ 4 ] [ j ] dp[4][j] dp[4][j]中取最小值即可。
代码
#include <bits/stdc++.h>
using namespace std;
#define inf 1000000000
typedef long long ll;
const int N = 150005;
int T, n[5], m[5], dp[5][N];
vector <int> e[5][N];
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
for (int i = 1; i <= 4; ++i) cin >> n[i];
for (int i = 1; i <= 4; ++i) {
for (int j = 1; j <= n[i]; ++j) {
cin >> dp[i][j]; //直接用dp数组记录价格,可以不用另外开price数组
}
}
for (int i = 2; i <= 4; ++i) {
cin >> m[i];
for (int j = 1; j <= m[i]; ++j) {
int x, y;
cin >> x >> y;
e[i][y].push_back(x); //第i道菜中的y与第i-1道菜的x互斥
}
}
for (int i = 2; i <= 4; ++i) {
map<int, int> c;
for (int j = 1; j <= n[i - 1]; ++j) {
c[dp[i - 1][j]]++; //预处理,对处理i-1道菜可能花费的价格计数
}
for (int j = 1; j <= n[i]; ++j) {
for (auto k : e[i][j]) { //删除与j互斥的边
c[dp[i - 1][k]]--;
if (!c[dp[i - 1][k]]) c.erase(dp[i - 1][k]); //细节:如果删到0,需要删除该元素,否则会影响结果
}
if (!c.empty()) dp[i][j] += c.begin()->first; //加上满足条件的最小花费
else dp[i][j] = inf; //没有满足条件,赋为极大值
for (auto k : e[i][j]) { //把预处理的map还原
c[dp[i - 1][k]]++;
}
}
}
int ans = inf; //取最小值为答案
for (int i = 1; i <= n[4]; ++i) {
ans = min(ans, dp[4][i]);
}
if (ans == inf) cout << "-1";
else cout << ans;
return 0;
}
囤了好多写了一半的题解,希望自己能勤快点攻破它们的难题qwq