Codeforces Round 958 (Div. 2)A到D简单题解(c++,python)

竞赛链接:Dashboard - Codeforces Round 958 (Div. 2) - Codeforces

ProblemA

题目链接:Problem - A - Codeforces

简单题解加代码:

Python:
def II():
    return int(input())
def MII():
    return map(int, input().split())
'''
模拟方法
'''
def func_1(n: int, k: int) -> int:
    res = 0
    cur = 1
    while cur < n:
        res += 1
        cur += k - 1
    return res
'''
数学方法
'''
def func_2(n: int, k: int) -> int:
    return (n - 1 + k - 2) // (k - 1)
            
def main():
    t = II()
    res = []
    '''
    操作:是说给一个整数n,可以把n分成最多k项,每项相加的值为n,
        此时操作次数加一
        分成的k项每项也可以进行相同的操作,
    结果:把n分成n个1
    要求:求达到结果的最小操作次数
    '''
    for _ in range(t):
        n, k = MII()
        res.append(func_2(n, k))
    for r in res:
        print(r)

if __name__ == "__main__":
    main()

C++:
#include <iostream>
#include <vector>

using namespace std;
//模拟方法
int func_1(int n, int k){
    int res = 0;
    int cur = 1;
    while (cur < n){
        res++;
        cur += k - 1;
    }
    return res;
}
//数学方法
int func_2(int n, int k){
    return (n - 1 + k - 2) / (k - 1);
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin >> t;
    /*
    操作:是说给一个整数n,可以把n分成最多k项,每项相加的值为n,
        此时操作次数加一
        分成的k项每项也可以进行相同的操作,
    结果:把n分成n个1
    要求:求达到结果的最小操作次数
    */
    while(t--){
        int n, k;
        cin >> n >> k;
        cout << func_2(n, k) << '\n';
    }
    return 0;
}

ProblemB

题目链接:Problem - B - Codeforces

简单题解加代码:

Python:
def II():
    return int(input())
def MII():
    return map(int, input().split())
def I():
    return input()

#把连续的0全部变成一个0,最后比较0和1的次数即可
#需要注意多数的边界条件
def func(s: str) -> str:
    #标记量
    x = 1
    cnt_0 = 0
    cnt_1 = 0
    for i in range(len(s)):
        c = s[i]
        if c == '1':
            cnt_1 += 1
            x = 1
        if c == '0' and x == 1:
            cnt_0 += 1
            x = 0
    return "YES" if cnt_0 < cnt_1 else "NO"

def main():
    t = II()
    res = []
    '''
    操作:对于一个二进制数组,选择一段,对于这一段的所有数字变成这一段的(多数)
    多数概念:零的次数小于1多数就是1,否则为0
    结果:把数组变成[1],也就是只含一个数字1的数组
    要求:是否可以通过若干操作变成结果
    '''
    for _ in range(t):
        n = II()
        s = I()
        res.append(func(s))
    for r in res:
        print(r)

if __name__ == "__main__":
    main()

C++:
#include <iostream>
#include <vector>

using namespace std;
//把连续的0全部变成一个0,最后比较0和1的次数即可
//需要注意多数的边界条件
string func(string s){
    //标记量,连续的0就只用加一次
    int x = 1;
    int cnt0 = 0;
    int cnt1 = 0;
    for (auto c : s){
        if (c == '1'){
            cnt1 += 1;
            x = 1;
        }
        if (c == '0' && x == 1){
            cnt0 += 1;
            x = 0;
        }
    }
    if (cnt0 < cnt1){
        return "YES";
    }else{
        return "NO";
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin >> t;
    /*
    操作:是说给一个整数n,可以把n分成最多k项,每项相加的值为n,
        此时操作次数加一
        分成的k项每项也可以进行相同的操作,
    结果:把n分成n个1
    要求:求达到结果的最小操作次数
    */
    while(t--){
        string s;
        int n;
        cin >> n;
        cin >> s;
        cout << func(s) << '\n';
    }
    return 0;
}

ProblemC

题目链接:Problem - C - Codeforces

简单题解加代码:

Python:

def II():
    return int(input())
def MII():
    return map(int, input().split())
def I():
    return input()

'''
    输入:整数n
    目标:最长的数组a,数组a每一位都小于n,严格递增,
        相邻两位按位或操作结果为n
    输出:数组a长度
        数组a元素

'''
'''
    1.我们从最高位开始,找出n的二进制表示中所有为1的位。

    2.对于每个为1的位,我们将其对应的值从n中减去,得到一个小于n的数。
    这样可以保证相邻两个数按位或的结果为n
'''
def func(n: int) -> list[int]:
    res = []
    x = 1
    while x <= n:
        x <<= 1
    
    while x:
        x >>= 1
        if (x & n != 0) and (x != n):
            res.append(n - x)
    res.append(n)

    return res

def main():
    t = II()
    result = []
    for _ in range(t):
        n = II()
        arr = func(n)
        result.append(arr)
    
    for res in result:
        print(len(res))
        #打印列表中所有元素用空格分割
        print(*res)


if __name__ == "__main__":
    main()

C++:

#include <iostream>
#include <vector>

using namespace std;
/*
    输入:整数n
    目标:最长的数组a,数组a每一位都小于n,严格递增,
        相邻两位按位或操作结果为n
    输出:数组a长度
        数组a元素

*/
/*
    1.我们从最高位开始,找出n的二进制表示中所有为1的位。

    2.对于每个为1的位,我们将其对应的值从n中减去,得到一个小于n的数。
    这样可以保证相邻两个数按位或的结果为n
*/
//注意题目范围,C++里面要用long long
vector<long long> func(long long n) {
    vector<long long> res;
    long long x = 1;
    while (x <= n) {
        x <<= 1;
    }
    while (x) {
        x >>= 1;
        if ((x & n) && (x != n)) {
            res.push_back(n - x);
        }
    }
    res.push_back(n);
    return res;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);

    int t;
    cin >> t;

    while (t--) {
        long long n;
        cin >> n;

        vector<long long> res = func(n);

        cout << res.size() << '\n';
        for (int i = 0; i < res.size(); ++i) {
            if (i > 0) cout << ' ';
            cout << res[i];
        }
        cout << '\n';
    }

    return 0;
}

ProblemD

题目链接:Problem - D - Codeforces

简单题解加代码:

C++:

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <limits>

using namespace std;
#define N 300000
#define B 20

long long a[N + 50];//怪兽攻击力
long long f[N + 50][B + 3];//树中连接节点的边
basic_string<int> v[N + 50];//用于构建树的邻接表

void dfs(int x, int fa){
    long long pl[B + 3], pr[B + 3];
    //计算当前节点生命值损失
    for (int i = 1; i <= B; i++){
        f[x][i] = a[x] * i;
    }
    //遍历当前节点的子节点
    for (auto y : v[x]){
        if (y != fa){
            dfs(y, x);
            // 处理左右子树的最小值
            pl[0] = pr[B + 1] = 1e18;

            // 计算左子树每一轮被击杀的最小值
            for (int i = 1; i <= B; i++) {
                pl[i] = pr[i] = f[y][i];
            }
            
            // 计算左子树每一轮被击杀的最小值
            for (int i = 1; i <= B; i++) {
                pl[i] = min(pl[i], pl[i - 1]);
            }
            
            // 计算右子树每一轮被击杀的最小值
            for (int i = B; i >= 1; i--) {
                pr[i] = min(pr[i], pr[i + 1]);
            }
            
            // 更新当前节点的损失值,选择左右子树最小的额外损失
            for (int i = 1; i <= B; i++) {
                f[x][i] += min(pl[i - 1], pr[i + 1]);
            }
        }
    }
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    int t;
    cin >> t;
    /*
    输入:怪物数量
        每个怪物的攻击力
        节点连接情况
    条件:每一轮会受到所有存活怪物的攻击,攻击后,
        可以选择在树结构中不直接相连的怪物
        死去的怪物下一回合不会攻击
    目标:生命值减少的最小值
    */
    while (t--){
        long long n;
        cin >> n;
        for (int i = 1; i <= n; i++){
            cin >> a[i];
        }
        long long j,k;
        for (int i = 1; i < n; i++){
            cin >> j >> k;
            v[j] += k;
            v[k] += j;
        }
        dfs(1, 0);
        long long res = 1e18;
        for (int i = 1; i <= B; i++){
            res = min(res, f[1][i]);
        }
        cout << res << '\n';
        //清空邻接表
        for (int i = 1; i <= n; i++){
            v[i] = {};
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值