Educational Codeforces Round 104 (Rated for Div. 2) A~E

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(n1)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=a2b 的整数三元组(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=a2b ………… ②

① - ②, 得: c 2 − c = b 2 + b c^2 - c = b^2 + b c2c=b2+b

c ∗ ( c − 1 ) = b ∗ ( b + 1 ) c * (c-1) = b*(b+1) c(c1)=b(b+1)

b = c − 1 b = c - 1 b=c1 …………③

将③代入①,得 a 2 = 2 c − 1 a^2=2c-1 a2=2c1

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 aibicidi种菜品可以选择,且知道它们分别的价格。相邻两种菜可能存在互斥关系,即不能同时上桌。给出四道菜分别有多少菜品可选以及它们的价格、互斥关系,求最少用多少钱可以构出一桌晚餐。

思路

显然,菜品之间存在转换关系。考虑状态转移:

对于第i道菜的第j种菜品,选择前i-1道菜合法的、花费最小的方案。

暴力的思路就是对于 d p [ i ] [ j ] dp[i][j] dp[i][j],遍历 d p [ i − 1 ] [ k ] dp[i-1][k] dp[i1][k],找出 k 、 j k、j kj不互斥中 d p [ i − 1 ] [ k ] dp[i-1][k] dp[i1][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[i1][k]+price[i][j]

显然,时间复杂度不允许。那么,我们的问题转变为:如何快速找到我们所需要的k?

map可以解决这个问题。首先,map自带排序,我们只需要在进入第 i i i道菜时,在遍历该工序每种菜之前预处理一个map计数数组,把上一层工序的所有所需花费 d p [ i − 1 ] [ j ] dp[i-1][j] dp[i1][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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值