算法设计与分析重难点题目讲解:(包含题目,解题思路,方法和代码)个人原创笔记如果有错请谅解

Problem1A.Factorial

时间限制:1000ms

空间限制:256MB

题目描述

给定正整数x,请给出x的值。

其中x代表x的阶乘,一个正整数的阶乘(factorial)是所有小于及等于该数的正整数的积。

例如4=1×2×3×4=244=1×2×3×4=24

注意:本题包含多组询问

输入格式

第一行输入一个正整数T,代表询问组数。

下面T行每行一个正整数x,代表询问计算阶乘的正整数。

输出格式

输出T行,每行一个整数,代表当前询问的结果x。

由于结果可能很大,请输出x对998244353取模的结果。

样例输入

3

10

25

100

Copy

样例输出

3628800

254940118

35305197

Copy

数据规模与约定

对于60%的数据,T=1

对于100%的数据,1≤T≤105,1≤x≤106

解题思路

1.预处理阶乘和模运算:由于x的最大值可能达到106,可以预先计算从1到106的结果,并对每个结果进行模运算,存储在数组中。这样做的好处是对于每个查询,我们可以直接从数组中获取结果,极大地提高了效率。

2.使用递推公式:

这样可以从1递增到106,依次计算并保存每个阶乘的模。

4.处理输入和输出:

•首先读入查询次数T。

•接着对于每个查询读入x,直接从预处理的数组中读取x%998244353的结果并输出。

复杂度分析

•时间复杂度:预处理阶乘的时间复杂度是O(n),其中n为106。查询每个结果的时间复杂度是O(1)。

•空间复杂度:需要O(n)的空间存储阶乘结果。

Python代码:

MOD=998244353

defprecompute_factorials(n):

factorial=[1]*(n+1)

foriinrange(2,n+1):

factorial[i]=(factorial[i-1]*i)%MOD

returnfactorial

defmain():

importsys

input=sys.stdin.read

data=input().split()

T=int(data[0])

queries=list(map(int,data[1:T+1]))

max_x=max(queries)

factorial=precompute_factorials(max_x)

results=[str(factorial[x])forxinqueries]

print("\n".join(results))

if__name__=="__main__":

main()

Problem1B.p进制数1

时间限制:1000ms

空间限制:256MB

题目描述

最近小G同学正在醉心于p进制数的研究。关于p进制数(...w2w1w0)p,它与1010进制具有如下的转换关系:x=∑wi∗pi,其中xi∈{0≤xi<p},wi∈{0≤wi<p}.

今天,小G同学正在研究p进制的取模问题,具体来说是这样的。给定一个p进制数(K)p,想要求得它被(D)p整除后的余数(R)p.也就是找到一个x∈[0,D),x∈Z,x≥0,使得K=D∗Q+R.R∈[0,D),Q∈Z, s.t.K=D∗Q+R.输出这个R的p进制表示。特别地,这里的D在p进制表示下各个数位之和为1,也就是∑wi=1.

现在,请你帮助小G同学使用计算机对该问题求解。

数据格式

输入

一行,三个正整数p,K,D.其中p,K,D是p进制下的表示。

输出

一个正整数R,它是在p进制下表示的。不要包含前导零。

样例

输入

114511

Copy

输出

0

Copy

数据范围及约定

2≤p≤16.

p,K,D的长度均不超过105

p,K,D均不包含前导零。

解题思路:

1.    输入处理:将给定的p进制数K和D转换为十进制数值。

2.    转化为十进制:利用权重公式,将p进制数K和D转换成十进制的数值。

3.    取余操作:使用十进制数进行余数计算。余数=K%D。

4.    将结果转回p进制:将结果转换为p进制表示输出。

代码实现:

```cpp

#include<iostream>

#include<vector>

#include<string>

#include<algorithm>

// 将p进制数转换为十进制

long long toDecimal(const std::string& num, int p) {

    long long res = 0;

    for (char c : num) {

        res = res * p + (isdigit(c) ? c - '0' : c - 'A' + 10);

    }

    return res;

}

// 将十进制转换为p进制

std::string fromDecimal(long long num, int p) {

    if (num == 0) return "0";

    std::string res;

    while (num) {

        int rem = num % p;

        res.push_back(rem < 10 ? '0' + rem : 'A' + rem - 10);

        num /= p;

    }

    std::reverse(res.begin(), res.end());

    return res;

}

int main() {

    int p;

    std::string K, D;

    std::cin >> p >> K >> D;

    // 将K和D从p进制转换为十进制

    long long K_decimal = toDecimal(K, p);

    long long D_decimal = toDecimal(D, p);

    // 计算余数

    long long R_decimal = K_decimal % D_decimal;

    // 将结果从十进制转换为p进制

    std::string R_p = fromDecimal(R_decimal, p);

    std::cout << R_p << std::endl;

    return 0;

}

```

解释代码:

- toDecimal函数:将p进制数转为十进制。

- fromDecimal函数:将十进制数转回p进制。

- main函数:

    1. 输入p、K和D。

    2. 利用toDecimal将K和D转为十进制。

    3. 计算余数R_decimal。

    4. 利用fromDecimal将余数转换为p进制输出。

Problem1C.一山不容二虎

时间限制:1000ms

空间限制:256MB

题目描述

一座山头容不得两只老虎,除非他们一公一母

随着时代变迁,世界上的老虎越来越少了呢。为了保护老虎,人类在登陆火星后,特地为老虎搭建了一片区域

为了美观与方便管理,这片区域由 n 行 m 列个牢笼组成,每个牢笼都很大,以方便老虎活动。

资深老虎专家说,放置老虎时,需要注意以下两点:

- 每个牢笼最多放置一只老虎,否则多只老虎在一起就会打架

- 每只老虎会时刻注视着自己所在的行和列。

1. 对于某只老虎,若该行只有自己,它会很高兴;

2. 如果该行还有另外一只老虎,它会主观认为该老虎与自己性别不同,所以也会很高兴;

3. 如果这一行,还有另外两只及以上的老虎,它们会感受到同性的威胁而心情崩溃。

4. 对于每一列,同理

假如你是老虎保护协会的一把手,负责把任意只老虎放在这片区域里,也可以不放,使得每只老虎的心情都很好,请问有多少种放置方法。

输入格式

一行包含两个整数 n, m,之间由一个空格隔开。

输出格式

总共的方案数,由于该值可能很大,只需给出方案数模99999739999973的结果。

样例#1

样例输入#1

13

Copy

样例输出#1

7

Copy

提示

样例说明

除了 33 个牢笼里都放置老虎以外,其它方案都是可行的,所以一共有 2×2×2−1=72×2×2−1=7 种方案。

数据规模与约定

- 对于 30% 的数据, n 和 m 均不超过 6;

- 对于 50% 的数据, n 和 m 至少有一个数不超过 8;

- 对于 100% 的数据,1≤n,m≤1001≤n,m≤100。

解题思路:

这道题的关键是如何正确放置老虎,使得每只老虎的心情都很好。主要思路包括:

1. 每行每列最多2只老虎:同一行或同一列最多只能放置两只老虎,且必须为一公一母。

2. 不放老虎的牢笼:允许某些牢笼不放置老虎。

可以将每行、每列视作一个独立的组合来考虑。具体步骤如下:

1. 计算每一行可能的组合方案。

- 一行为空或有一只老虎,有2种选择。

- 一行有两只老虎,则要确保是不同性别,有1种选择。

- 因此,每行的组合方式有3种:0、1、2。

2. 计算每一列的组合方式,与行类似。

3. 将行与列的组合方式相乘,得到总的组合方案数。

4. 使用组合数公式(3^n-1)*(3^m-1)%9999973计算答案,其中3^n表示每一行或每一列的组合数。

#include<iostream>

const int MOD = 9999973;

//快速幂计算(base^exp)%MOD

long long quickPower(long long base, int exp) {

    long long result = 1;

    while (exp) {

        if (exp & 1)

            result = (result * base) % MOD;

        base = (base * base) % MOD;

        exp >>= 1;

    }

    return result;

}

int main() {

    int n, m;

    std::cin >> n >> m;

    //每一行或列可以有0,1,或2只老虎

    long long rowWays = quickPower(3, n);

    long long colWays = quickPower(3, m);

    //减去空状态

    long long result = (rowWays - 1) * (colWays - 1) % MOD;

    std::cout << result << std::endl;

    return 0;

}

代码解释:

1. quickPower函数实现快速幂运算(base^exp)%MOD,用于计算组合方式。

2. 计算每行、每列的组合数并减去空状态组合。

3. 输出结果。

复杂度分析:

- 时间复杂度:快速幂算法O(log(max(n,m)))。

- 空间复杂度:常数级别的空间消耗

Problem1D.又是高精度?!

 时间限制:1s 空间限制:2,000,000KiB 题目描述 2024.3.282024.3.28清晨,在听过世界上第一个程序员——阿达的故事后,原力清理大师深受感动,决定为计算机的发展做出自己的贡献。阿达早就发展出了�++C++的雏形,只不过受限于当时计算机仍沿用十进制导致的精度限制,一直没能付诸实践。众所周知,当前计算机大多是6464位的。为了发展计算机科技,首先我们应当提升计算机的计算精度至10241024位,以免再次埋没阿达这样的人才。 要提升精度,首先就要完成10241024位22进制数字的计算,而这其中以求和最为简单。但是,原力清理大师非常讨厌高精度,所以他希望你帮他求求以下�T组二进制数的和。由于他没这么多内存空间保存这些数字,故而每一组求和之后的结果需以十进制的格式输出,并且需对10000000071000000007取模。 输入格式 § 第一行一个整数�T,代表原力清理大师向你询问的次数。 § 接下来2∗�2∗T行中,第2∗�−12∗i−1行与第2∗�2∗i行(1≤�≤�)(1≤i≤T)分别为两个字符串,且为两个二进制数。 输出格式 § �T行,每行一个十进制数,代表计算结果取模之后的结果。 样例数据#1: 1 10011 101 Copy 样例输出#1: 24 Copy 样例解释: 1001110011转换为十进制后为1919,101101转换为十进制后为55,两数之和为2424,取模后仍为2424,故输出2424。 数据范围与约定 对于20%20%的数据,保证第�i组输入字符串的长度���(1≤�≤�)len(1≤i≤T)满足1≤���≤101≤len≤10,输入数据组数�T满足10≤�≤10010≤T≤100; 对于100%100%的数据,保证第�i组输入字符串的长度���(1≤�≤�)len(1≤i≤T)满足1≤�≤10241≤n≤1024,输入数据组数�T满足10≤�≤1000010≤T≤10000; 注意:为了防止数据过大而导致���TLE,建议在读入数据前加入该代码,取消输入输出流同步~ ios::sync_with_stdio(false); ios::sync_with-stdio(0),cin.tie(0),cout.tie(0); Copy 样例输入只有一组,并不符合输入数据范围~

这道题目要求计算大量组二进制数的和,并输出每组求和结果的模109+7109+7的值。因为二进制数的长度最大可达1024位,所以直接处理整数可能会导致溢出。我们可以利用Python的内置支持处理大数的能力,读入二进制字符串,转换为整数,进行求和,最后取模输出。

解题步骤:

1. 首先,读入总的询问次数T。

2. 对于每组询问,读入两个二进制字符串。

3. 使用Python的内置函数int()将这两个二进制字符串转换成整数。

4. 将这两个整数相加,并取模109+7109+7。

5. 输出结果。

这里的关键是利用Python的能力来处理大整数运算和对每一对数进行加法和模运算。

```python

import sys

# 读入所有输入数据

input_data = sys.stdin.read().split()

MODULO = 1000000007

T = int(input_data[0])  # 第一个元素是 T

results = []

index = 1

# 对于每组询问

for _ in range(T):

    bin1 = input_data[index]

    bin2 = input_data[index + 1]

    # 将两个二进制字符串转换为十进制整数

    num1 = int(bin1, 2)

    num2 = int(bin2, 2)

    # 计算和并取模

    result = (num1 + num2) % MODULO

    results.append(result)

    index += 2

# 输出所有结果

for result in results:

    print(result)

```

这段代码使用了`sys.stdin.read()`来一次性读取所有输入数据,这样可以在处理大量数据时减少输入的开销。然后,我们按行处理每对二进制数,将它们转换成整数,求和,取模,最后输出结果。

Problem1E.子串计数

题目描述

给定仅包含小写字母的字符串 s,求 s 的所有子串中,恰好包含 k 个不同字母的子串数量。

输入格式

第一行包含一个字符串 s。

第二行包含一个正整数 k。

输出格式

一个非负整数,表示满足要求的子串数量。

样例输入1

abcacb

2

样例输出1

6

样例1解释

包含:ab, bc, ca, ac, cb和cac六种

样例输入2

uwuowouwu

3

样例输出2

25

样例输入3

abcacb

1

样例输出3

6

样例输入4

thequickbrownfoxjumpsoveralazydog

10

样例输出4

28

数据范围及约定

对于60%的数据,1 ≤ |s| ≤ 103

对于100%的数据,1 ≤ |s| ≤ 106,1 ≤ k ≤ 26

字符串仅由小写字母组成。

要解决这个问题,我们需要计算给定字符串中所有恰好包含 k 个不同字母的子串的数量。由于直接枚举所有子串并检查其包含的不同字母的数目的复杂度非常高,特别是对于长度达到 106 的字符串,我们需要使用一种更高效的方法。

滑动窗口+双指针方法

核心思想是使用滑动窗口(也称为双指针方法)来有效地检测具有 k 个不同字符的子串数量。这个方法允许我们在不重复计算的情况下移动子串的起点和终点,从而动态地更新窗口中不同字符的计数。

算法步骤:

1. 定义窗口: 使用两个指针 left 和 right 来定义窗口的边界。起始都在字符串的起始位置。

2. 拓展窗口: 向右移动 right 指针来增加新的字符,直到窗口中包含 k 个不同的字符。

3. 收缩窗口: 当窗口中的不同字符数超过 k 时,向右移动 left 指针来减少字符直到窗口中恰好有 k 个不同字符。

4. 计数有效子串: 每次当窗口恰好包含 k 个不同字符时,以 right 指针为结束点的子串数量等于 right - left + 1。

5. 记录和更新: 使用一个字典来记录窗口中每个字符的出现次数,并相应地更新。

代码实现:

```python

def count_substrings_with_k_distinct(s, k):

    from collections import defaultdict

    def atMostKDistinct(k):

        count = defaultdict(int)

        res = 0

        left = 0

        for right in range(len(s)):

            if count[s[right]] == 0:

                k -= 1

            count[s[right]] += 1

            while k < 0:

                count[s[left]] -= 1

                if count[s[left]] == 0:

                    k += 1

                left += 1

            res += right - left + 1

        return res

    return atMostKDistinct(k) - atMostKDistinct(k - 1)

# 使用标准输入读取数据并调用函数输出结果

import sys

input_data = sys.stdin.read().split()

s = input_data[0]

k = int(input_data[1])

print(count_substrings_with_k_distinct(s, k))

```

此代码首先定义了一个 atMostKDistinct 辅助函数,该函数计算最多包含 k 个不同字符的子串数量。通过计算"最多包含 k 个字符的子串数量"减去"最多包含 k-1 个字符的子串数量",我们可以精确地获取"恰好包含 k 个不同字符的子串数量"。这种方法相比直接枚举所有可能的子串要高效得多。

Problem1F.棋盘

题目描述

有一个大小为 n×m 的矩形棋盘。从上到下,第一行到第 n 行编号为1到n;从左到右,第一列到第 m 列编号为1到m。

第 i 行和第 j 列交点处的单元格包含数字 i^j( i 的 j 次幂)。以 n=3 和 m=3 为例,棋盘如下:

1 1 1

2 4 8

3 9 27

求写在棋盘上的不同整数的个数。

输入格式

输入一行包含两个整数 n 和 m(1≤n,m≤10^6)-棋盘的行数和列数。

输出格式

输出一个整数,即棋盘上不同整数的个数。

样例输入1

3 3

样例输出1

7

样例输入2

2 4

样例输出2

5

数据范围与约定

1≤n,m≤10^6

解题思路:题目要求计算 n×m 大小的棋盘上,由 i^j 的不同值的数量。这里 i 代表行数,从1到 n ;j 代表列数,从1到 m 。题目实际要求计算 (i^j) 组合出现的唯一值数量。

在不同条件下:

1. n 和 m 都较小:可以通过直接计算并使用集合存储结果,但这种方法不适用于 n 和 m 非常大的情况。

2. 更优方法:

- 对于 n×m 数组,我们需要确定 1^j 到 n^m 之间的所有独特组合。

- 对于每一行,固定 i 从1到 n ,计算每一列的组合。

优化思想:

- 使用最大公约数(GCD)来减少重复计算,例如,如果 gcd(i,j)=1 则结果会唯一。

代码实现:使用 Python 或者 C++ 实现以下代码可以快速计算唯一值数量。

代码示例(Python):

```python

def count_unique_powers(n,m):

    seen=set()

    for i in range(1,n+1):

        for j in range(1,m+1):

            seen.add(i**j)

    return len(seen)

#输入

n,m=map(int,input().split())

#计算并输出唯一值的数量

print(count_unique_powers(n,m))

```

该代码直接遍历每个 i 和 j 的组合,然后将其结果存储到 seen 集合中以确保唯一值。

但在实际应用中,对于 n 和 m 较大时,此代码效率低。需要考虑进一步优化的策略,例如:

- 基于 prime factor 的快速检测。

- 根据最大 GCD 减少不必要的计算。

Problem2A.序列中未出现的最小的非负整数

时间限制:2000ms

空间限制:256MB

题目描述

我们定义一个函数mex为序列中未出现的最小的非负整数

例如mex({1,2,3})=0mex({0,2,1,4,3})=5mex({2,1,0,4})=3mex({0,1,1,3})=2

现在给定一个序列,请你求出该序列的mex函数值。

输入格式

第一行一个整数n,代表序列A的长度。

第二行包含n个非负整数,用空格隔开,代表该序列A的n个数。

输出格式

输出一个整数,代表该序列的mex函数值。

样例输入

3

0 1 3

Copy

样例输出

2

Copy

数据规模与约定

对于60%的数据,1≤n≤103,序列中的每个数Ai满足0≤Ai≤103

对于100%的数据,1≤n≤106,序列中的每个数Ai满足0≤Ai≤106

解题思路:题目要求找到序列中未出现的最小非负整数,也就是mex(MinimumExcludedValue)。可以使用以下步骤进行计算:

1.    遍历序列:遍历所有元素,找出所有出现过的数,并标记它们。

2.    建立布尔数组:通过布尔数组seen标记每个数是否在序列中出现。

3.    寻找最小未出现的数:从0开始,依次检查每个数是否在seen中存在,返回第一个未被标记的数。

代码实现:我们可以使用Python或C++实现该算法。

Python代码:

def find_mex(arr):

    n = len(arr)

    seen = [False] * (n + 1)

    # 标记序列中出现的每个数

    for num in arr:

        if num <= n:

            seen[num] = True

    # 寻找第一个未被标记的数

    for i in range(n + 1):

        if not seen[i]:

            return i

# 输入

n = int(input())

arr = list(map(int, input().split()))

# 输出最小未出现的非负整数

print(find_mex(arr))

代码解释:

1.    输入读取:通过input()读取输入序列的长度n以及序列arr。

2.    布尔数组:创建布尔数组seen来记录某数是否在arr中出现。

3.    遍历标记:遍历arr,将出现过的数标记为True。

4.    查找MEX:从0开始,依次检查seen数组中第一个未被标记为True的数。

复杂度分析:

•      时间复杂度:O(n),遍历一遍数组来标记,然后再检查一遍。

•      空间复杂度:O(n),用于存储布尔数组seen。

Problem2B.

问题描述

在小G同学解决了p进制的取模问题之后,他又开始研究负数进制的问题,也就是说∑wipi中的p<−1。当然,小G同学喜欢从特殊到一般,所以他这次会研究−2进制的问题。他注意到如下对应关系:

10   2     -2

-1    11   11

-2    10   10

-3    1101       1101

显然,−2进制可能与2进制的补码有很深的联系,于是他就请你帮忙。他希望每次向你询问一个2进制数的−2进制,而你只需要将它写出来。

数据格式

输入

第一行一个正整数T,表示测试用例的个数。

每个测试用例一行,一个0101串表示一个有符号二进制数。最高位为符号位,参考c/c++的补码,可能包含前导零。

输出

每个测试用例一行,一个0101串表示一个负二进制数,不得包含前导零。

样例

输入

5

00

01

11

10

1101

输出

0

1

11

10

1101

数据范围及约定

输入的0101串不超过32位。

这道题目要求将一个二进制数的补码(以负二进制形式表示)转换成负二进制数。由于数是以补码形式给出的,首先需要解读补码表示的实际整数值,然后再将该整数转换为负二进制表示。

解析补码

对于补码(2'scomplement)表示的二进制数:

如果最高位(符号位)是0,它表示正数,其值就是按二进制直接转换的结果。

如果最高位是1,它表示负数,其值是取反(除最高位外)后加1的结果的负数。

负二进制转换

负二进制数,与标准的二进制数不同,其进位规则是基于基数-2而非2。对于一个给定的十进制数𝑛,转换到负二进制的方法是:

1. 𝑛除以-2,记录余数。

2. 如果余数为负数,将余数调整为正数,相应的商增加。

3. 更新𝑛为商。

4. 重复步骤1-3,直到𝑛0

转换完成后,余数序列(倒序)即为负二进制数。

Python实现

```python

def complement_to_decimal(bits):

    if bits[0]=='0': # 正数或0

        return int(bits, 2)

    else: # 负数的补码表示

        n = len(bits)

        # 转换补码为原码(十进制)

        return -((1 << n) - int(bits, 2))

def decimal_to_negabinary(n):

    if n == 0:

        return "0"

    digits = []

    while n != 0:

        remainder = n % -2

        n = n // -2

        if remainder < 0:

            remainder += 2

            n += 1

        digits.append(str(remainder))

    return ''.join(digits[::-1]) # 反转列表并连接为字符串

# 输入处理

import sys

input = sys.stdin.read

data = input().split()

T = int(data[0])

results = []

for i in range(1, T+1):

    bin_complement = data[i]

    dec_value = complement_to_decimal(bin_complement)

    negabinary = decimal_to_negabinary(dec_value)

    results.append(negabinary)

for result in results:

    print(result)

```

以上Python代码首先定义了将补码转换为十进制数的函数complement_to_decimal,然后定义了将十进制数转换为负二进制的函数decimal_to_negabinary。最后处理输入,对每一个测试案例应用这两个函数,然后输出结果。

Problem2C.

题目描述

Monster最近很讨厌做数学题,所以决定让YQJ帮他做。

Monster将会给定一个除法表达式F,F=1/2/3…/n=F=X1/X2/X3…/Xn,其中Xi是正整数并且Xi≤109,显然想要用程序计算这个式子,应该从左往右依次计算。

但是现在YQJ也想开摆,所以他想要在式子中添加括号,使得整个算式最终的结果是整数,这样最终输出时就不用输出//了。

例如1/2/1/2=1/41/2/1/2=1/4。

但是现在YQJ想要更偷懒一步,让你只判断,是否能够在式子中适当地添加括号使得结果为整数,也就是说,你只要输出YES或者NO。

当然,YQJ为了防止某种Hack数据的恶劣手法,采用了多组数据。

输入格式

第一行包括一个整数T,表示将有T组数据。

每组数据将先给出一个数字n,表示表达式中将有n个数相除。

接下来n行,每行一个数Xi。

输出格式

输出T行,满足条件则输出YES,否则输出NO。

样例输入1

1

4

1

2

1

2

Copy

样例输出1

YES

Copy

样例1解释

1/2/1/2改成(1/2)/(1/2)则满足要求

样例输入2

1

3

1

2

3

Copy

样例输出2

NO

Copy

数据范围及约定

对于20%的数据,T=1,1≤n≤104。

对于100%的数据,1≤T≤10,1≤n≤104。

解题思路:题目给定多个测试用例,每个用例由一组整数组成,这些整数按顺序形成连除表达式。目标是判断是否可以在表达式中添加括号,使得整个表达式结果为整数。

思路:

• 如果所有数字都是1,结果显然为整数。

• 若存在不为1的数,且此数的前面没有其他非1数,则结果可以为整数。例如:1/2/1可以为1/(2/1)。

• 但是,如果多个非1数连在一起,则结果不能为整数。例如:2/3/1,无论如何添加括号都不行。

2.    算法步骤:

• 遍历每个测试用例,查看数组中存在多少个非1数字:

• 若有0或1个非1数,输出"YES"。

• 否则,输出"NO"。

代码实现:

```python

def can_be_integer(arr):

    non_one_count = sum(1 for x in arr if x != 1)

    return non_one_count <= 1

T = int(input())

for _ in range(T):

    n = int(input())

    arr = [int(input()) for _ in range(n)]

    if can_be_integer(arr):

        print("YES")

    else:

        print("NO")

```

代码解释:

1. 函数can_be_integer:计算数组中非1数字的数量,若数量不超过1,则可以通过添加括号使得结果为整数。

2. 输入输出:读取每个测试用例的输入,逐个计算并输出结果。

复杂度分析:

• 时间复杂度:O(T*n),其中T为测试用例数量,n为每个测试用例的元素个数。

• 空间复杂度:O(n),用于存储输入数据。

---

希望这样的格式对您有帮助!

Problem2D.Nikuhn的破烂模拟计划

题目背景

我在五点二十睡觉,十三点十四准时起,主打个浪漫,沉溺在爱河不上岸,爱你在本职里。

你一定刷到过以此为bgm的视频,并用这个bgm跳了一段鬼步舞給女神表白,然后被拒了()

题目描述

Nikuhn很喜欢这种表达,所以他模仿这种表达方式,用一个字符串来描述自己的计划。

这个字符串包含两串数字和若干字母,字母是中文的首字母,两串数字则表示两个时刻。字符串蕴含的信息是,Nikuhn在这两个时刻间将要做某件事。

为了美观与神秘,Nikuhn会省略小时或分钟的前缀零,并将该操作称为美化。例如00:0202:00美化后均为2,又如01:0100:1111:00美化后均为11

由于Nikuhn的精力有限,所以不会工作多于24h;又为了保持效率,所以至少做事6h。同时Nikuhn只会把计划安排在一天内。

现在请你推测出,Nikuhn做这件事花费的时间是多少,并将时间段按Nikuhn的美化方式来表述,如果美化后有多个结果,请把不同的美化结果按升序输出。如果没有符合条件的时间,请输出-1

输入与输出

输入格式

输入一个字符串s

输出格式

输出若干数字,用空格隔开

样例输入1

wz520sj1314zsq

Copy

样例输出1

754

Copy

样例输入2

hhhle12sjggg23zsqadad

Copy

样例输出2

1121582248

Copy

样例解释

在样例一中,
第一个时间只能描述为05:20,第二个时间只能描述为13:14,因此时间差只能为07:54,按照Nikuhn的美化方式,你要省略前缀零,因此为754

在样例二中,
第一个时间可以描述为001201021200,第二个时间段可以描述为002302032300
因此,合适的时间差为110022482158,省略前缀零后,为1121582248

Limitation

s的长度小于100

提取数字信息:首先,从字符串中提取出所有数字序列,因为字符串长度小于100,通常我们会提取出两个数字序列。

生成可能的时间:对于每个数字序列,考虑所有可能的时间格式(小时和分钟)。例如对于数字12,可能的时间包括00:12,12:00,01:02等。

计算时间差:根据提取出的时间点,计算它们之间的可能时间差,注意只考虑6至24小时的差。

美化时间差:将得到的时间差格式化,去除前导零,并以整数形式表达。

输出所有唯一且符合条件的时间差:按照从小到大的顺序输出结果,如果没有任何符合的时间差,输出-1。

importre

defextract_possible_times(digits):

n=len(digits)

possible_times=set()

foriinrange(1,n):

hour_part=int(digits[:i])

minute_part=int(digits[i:])

ifhour_part<24andminute_part<60:

possible_times.add((hour_part,minute_part))

hour_part=int(digits[i:])

minute_part=int(digits[:i])

ifhour_part<24andminute_part<60:

possible_times.add((hour_part,minute_part))

returnpossible_times

defcalculate_time_diff(time1,time2):

h1,m1=time1

h2,m2=time2

total_minutes1=h1*60+m1

total_minutes2=h2*60+m2

diff=abs(total_minutes2-total_minutes1)

ifdiff<6*60ordiff>24*60:

returnNone

ifdiff>12*60:

diff=24*60-diff

hours=diff//60

minutes=diff%60

returnhours,minutes

defbeautify_time(hours,minutes):

result=""

ifhours>0:

result+=str(hours)

ifminutes>0orhours==0:

ifminutes<10andhours>0:

result+='0'

result+=str(minutes)

returnint(result)

defsolve(s):

#正则提取数字序列

numbers=re.findall(r'\d+',s)

iflen(numbers)!=2:

print(-1)

return

times1=extract_possible_times(numbers[0])

times2=extract_possible_times(numbers[1])

results=set()

fort1intimes1:

fort2intimes2:

diff=calculate_time_diff(t1,t2)

ifdiff:

beautified=beautify_time(*diff)

results.add(beautified)

ifnotresults:

print(-1)

else:

print("".join(map(str,sorted(results))))

#示例输入

solve('wz520sj1314zsq')#应输出:754

solve('hhhle12sjggg23zsqadad')#应输出:1121582248

这个解法首先定义了几个辅助函数:extract_possible_times用于从数字序列中生成所有合法的时间;calculate_time_diff用于计算两个时间点的差值;beautify_time用于将时间差美化,去除前导零。最终的solve函数将这些组合起来,按顺序执行以上的步骤。

窗体顶端

Problem2E.子树最大值查询

题目描述

现有一棵n点的树,每个结点有一个权值��wi,初始情况下根为11

定义:结点的价值是该结点子树中,权值最大的结点的权值

回答q次询问,每次询问给出,u,v,请输出:

  • 若树以u为根,则v价值是多少?

输入格式

第一行两个整数,n,q,表示树的结点数和询问次数。

接下来一行n个整数,表示结点i的权值。

接下来−1n−1行每行两个整数,表示树的一条边。

接下来q行每行两个整数,u,v,表示一次询问。

输出格式

对于每次询问输出一行表示答案。

样例输入1

64

123456

13

16

26

34

35

41

63

24

44

Copy

样例输出1

6

5

4

6

Copy

样例1解释

对于第一组询问,33的子树中结点的权值分别是3,1,6,23,1,6,2,所以最大权值为66

对于第三组询问,44的子树中结点的权值44,最大权值为44

注意到该样例为了演示,各结点的权值等于编号,其他测试点可能不满足这一点。

image-20240421103930152

数据范围及限制

对于60%60%的数据,1≤,≤1031≤n,q≤103

对于100%100%的数据,1≤,≤5×105,1≤��,1≤,1≤n,q≤5×105,1≤win,1≤u,vn


这是一个关于树的数据结构和树上动态查询的高级问题。题目要求我们对于给定的树结构,根据不同的根节点设置,计算特定节点的子树中的最大节点权值。

解决思路

对于此问题,最核心的挑战在于有效处理动态根节点的变化,并且高效查询子树的最大权值。

预处理:

使用DFS(深度优先搜索)或BFS(广度优先搜索)来预计算每个节点作为根时其子树中所有节点的权值最大值。

为了处理动态根的改变,可以采用重心分解(CentroidDecomposition),它允许我们较快地根据当前视角的根节点重新计算子树权值。

在线处理查询:

对于每个查询(u,v),首先确定如果以u为根,v的子树是什么。

计算v的子树中的最大权值。

实施步骤

构建树:

使用邻接表来表示树。

DFS预处理:

对于树的每一个节点,计算以该节点为根的子树的所有节点权值的最大值。

处理查询:

对于每个查询(u,v),使用之前预处理的信息和可能的树结构调整(如果需要的话,可以使用LCA(最近公共祖先)和路径分解来辅助),来找到以u为根时v的子树的最大权值。

Problem2F.Changethelist

Description

给定一个包含n个正整数的数组。在一个回合中你可以选择任何元素并增加或减少1。目标是通过尽可能少的操作使数组严格增加。您可以以任何方式更改元素,可以变为负值或等于0

InputFormat

输入的第一行包含一个整数n(1 ≤ *n* ≤ 3000)—数组长度。

OutputFormat

下一行包含n整数��ai(1 ≤ ��ai ≤ 109109)

TestCase1

input1

7
215115911

Copy

output1

9

Copy

TestCase2

input2

5
54321

Copy

output2

12

Copy

Note

对于样例1,数组23567911可以为|2 - 2| + |1 - 3| + |5 - 5| + |11 - 6| + |5 - 7| + |9 - 9| + |11 - 11| = 9。

解题思路:这道题要求我们通过调整数组中的每个元素,使得它们形成一个严格递增的序列。每次操作中可以将某个元素增加或减少1,目标是最小化操作次数。

思路:

为了使数组严格递增,理想情况下每个元素应从小到大有序排列。

我们可以将问题视为计算数组到某个严格递增数组的差异,然后最小化这些差异。

具体步骤:

去重并排序:由于递增序列不能重复,因此我们可以先对输入数组进行排序并去重,生成一个目标递增序列。

动态规划:定义一个二维数组dp[i][j],其中dp[i][j]表示将前i个元素调整到目标序列中前j个元素的最小操作数。

转移方程:

dp[i][j]=min(dp[i-1][k]+abs(arr[i]-sorted_unique[j])),其中k范围在0到j。

结果:遍历二维数组,找到最小的dp值即为最小的操作次数。

代码实现:

defmin_operations_to_increase(n,arr):

sorted_unique=sorted(set(arr))

m=len(sorted_unique)

dp=[[float('inf')]*mfor_inrange(n)]

forjinrange(m):

dp[0][j]=abs(arr[0]-sorted_unique[j])

foriinrange(1,n):

min_previous=float('inf')

forjinrange(m):

min_previous=min(min_previous,dp[i-1][j])

dp[i][j]=min_previous+abs(arr[i]-sorted_unique[j])

returnmin(dp[n-1])

#输入读取

n=int(input())

arr=list(map(int,input().split()))

#输出最小操作数

print(min_operations_to_increase(n,arr))

代码解释:

去重排序:对数组进行去重排序,生成目标递增序列sorted_unique。

初始化dp数组:二维数组dp保存了每一步的操作次数。

转移方程计算:动态规划计算每一步的最小操作次数。

返回结果:计算并返回dp中的最小值。

复杂度分析:

时间复杂度:O(n*m),其中n是数组长度,m是sorted_unique长度。

空间复杂度:O(n*m),用于保存dp数组。

Problem 3A. 数圈圈

时间限制:1000ms

空间限制:256MB

题目描述

小添在纸上写下了一个数字k进制的数,10进制下对应的数为n。小添写下了这个数字后好奇这个数字里有多少个圈圈。具体来说是这样的,如‘0’有一个圈,‘B’有两个圈等等(大于10进制统一用大写字母)。

小添想让你统计一下这个k进制数有一共有多少个圈圈(不考虑前导零)。

输入格式

一行两个整数kn

输出格式

一个整数,表示一共有多少个圈。

样例输入1

10 80

Copy

样例输出1

3

Copy

数据范围及约定

对于100%的数据 0≤≤10182≤≤160n10182k16。

提示

数字

圈的个数

0

1

1

0

2

0

3

0

4

1

5

0

6

1

7

0

8

2

9

1

A

1

B

2

C

0

D

1

E

0

F

0

解题思路:

  1. 创建一个数组,存储每个数字对应的圈数。
  2. 将k进制的数转换为十进制。
  3. 遍历十进制数的每一位,根据该位的值在数组中查找对应的圈数并累加。
  4. 输出累加结果。

#include <iostream>

#include <vector>

#include <unordered_map>

using namespace std;

int main() {

    unordered_map<char, int> circles = {{'0', 1}, {'1', 0}, {'2', 0}, {'3', 0}, {'4', 1}, {'5', 0}, {'6', 1}, {'7', 0}, {'8', 2}, {'9', 1}, {'A', 1}, {'B', 2}, {'C', 0}, {'D', 1}, {'E', 0}, {'F', 0}};

   

 int k, n;

    cin >> k >> n;

   

    // Convert k-based number to decimal

    int decimal = 0;

    int base = 1;

    while (n > 0) {

        int digit = n % 10;

        decimal += digit * base;

        base *= k;

        n /= 10;

    }

   

    // Count circles

    int total_circles = 0;

    while (decimal > 0) {

        int digit = decimal % 10;

        total_circles += circles[digit + '0'];

        decimal /= 10;

    }

   

    cout << total_circles << endl;

   

    return 0;

}

Problem 3B. 我爱月光

时间限制:500ms

空间限制:64MB

题目背景

三日日同学特别喜欢出去玩,有多少钱花多少钱,这导致她每个月都没有剩余的钱,所以这个月生活费用完了她就会没钱吃饭

清明假期来临,这可是个放松的好时机,三日日同学在这个月的前几天吃了大餐,捏了脚,泡了澡,旅了游,导致她这个月的生活费所剩无几

三日日同学的经济状况每天只能吃两个菜,两个菜分别可以是荤菜或素菜一个素菜x元,一个荤菜y

由于三日日本性未改,所以她想尽可能尝试新花样,又想延续月光的传统,想必聪明的你一定有办法帮助三日日同学编程算出一共有多少种方案可以满足她的要求。

题目描述

已知这个月还剩下n天,三日日同学这个月还剩m元,一个素菜x元,一个荤菜y元,一天必须吃两道菜(可以全荤全素或荤素搭配),如果有有符合方案(n天内正好花完m)输出所有正好花完所有钱方案的种数,没有符合方案则输出"meishiwoyouqinqingka"(没事我有亲情卡)(不用输出引号)

数据保证=1yx=1

输入格式

输入4个整数分别表示n,m,x,y

输出格式

一个整数答案,表示可能的方案数

样例输入1

2 6 1 2

Copy

样例输出1

3

Copy

样例1解释

有如下共3种搭配可以

1122 2211 1212

样例输入2

1 100 1 2 

Copy

样例输出2

meishiwoyouqinqingka

Copy

样例2解释

很显然没有办法一天实现月光

数据范围及约定

对于60%测试点1≤<10,1≤≤800,1≤,≤401n<10,1m800,1x,y40

对于100%测试点:1≤≤31,1≤≤10000,1≤,≤1001n31,1m10000,1x,y100(怎么有人没钱了还去吃100元的菜)

题思路

这道题目可以通过动态规划的方式求解。主要思想是使用二维动态规划数组 dp[i][j],其中 i 代表天数,j 代表花费,dp[i][j] 存储在 i 天内花费 j 元的方案数量。

步骤

初始化: dp[0][0] = 1,表示在0天花费0元有一种方案。

状态转移:

对于每一天,我们有两种选择:吃两个素菜、吃两个荤菜、吃一个素菜一个荤菜。

由于题目中给出 y = x + 1,即荤菜比素菜贵1元,所以对于每一天的花费有三种可能:

两个素菜:花费 2x 元

两个荤菜:花费 2y = 2(x+1) = 2x + 2 元

一个素菜一个荤菜:花费 x + y = x + (x + 1) = 2x + 1 元

利用以上的花费计算状态转移:

如果吃两个素菜,则 dp[i][j] += dp[i-1][j-2x](如果 j-2x 非负)

如果吃两个荤菜,则 dp[i][j] += dp[i-1][j-2x-2](如果 j-2x-2 非负)

如果吃一个素菜一个荤菜,则 dp[i][j] += dp[i-1][j-2x-1](如果 j-2x-1 非负)

求解: 最终结果即为 dp[n][m],如果 dp[n][m] > 0 则输出这个值,否则输出 "meishiwoyouqinqingka"。

复杂度

时间复杂度:O(n * m),其中 n 是天数,m 是金额。 空间复杂度:O(n * m),用于存储动态规划表。

def solve(n, m, x, y):

    # 初始化dp数组

    dp = [[0 for _ in range(m + 1)] for _ in range(n + 1)]

    dp[0][0] = 1  # 在0天花费0元有1种方案

   

    # 动态规划填表

    for day in range(1, n + 1):

        for cost in range(m + 1):

            # 如果当前花费可以选择两个素菜

            if cost >= 2 * x:

                dp[day][cost] += dp[day - 1][cost - 2 * x]

            # 如果当前花费可以选择两个荤菜

            if cost >= 2 * y:

                dp[day][cost] += dp[day - 1][cost - 2 * y]

            # 如果当前花费可以选择一个素菜一个荤菜

            if cost >= x + y:

                dp[day][cost] += dp[day - 1][cost - (x + y)]

    # 检查是否有符合条件的方案

    if dp[n][m] > 0:

        return dp[n][m]

    else:

        return "meishiwoyouqinqingka"

# 示例调用

# 输出应为3

print(solve(2, 6, 1, 2))

# 输出应为"meishiwoyouqinqingka"

print(solve(1, 100, 1, 2))

Problem 3C. 源深,启动

时间限制:1000ms

空间限制:256MB

题目背景

Nikuhn最近在玩桃源深处有人家(简称源深),这是一款大型多人种田类休闲游戏。Nikuhn入坑一周内便升到了二十级,但第二周只升了3级。发现原来是经验值会随等级提高而爆炸式增长。Nikuhn于是特意寻找了等级与经验值的函数关系式,发现该关系式居然和离散数学集合论的某一函数息息相关。

题目描述

如果 x 代表等级,y 代表经验,那么 xy 符合下列规则:

��=/+()xb=y/z+f(t)

其中,由于某讯公司严格的保密机制,Nikuhn不知道 ()f(t) 的值是多少,只知道 ()f(t) 远小于 ���zxb

但他告诉你 xby 满足的另外一个关系:

在小于 ��xb 的正整数中,与 ��xb 互质的整数个数为 y。现在给你 x b 的值,请你输出 y  ��xb 作比的结果,以近似求出 z 的值。

看到这里,Nikuhn知道你已经乱了,那么请忽略上述情景

简单来说,令n1 = ��xb,令n2 表示在小于 n1 的正整数中,与 n1 互质的整数个数。请你求出n2 n1

输入与输出

输入格式

输入两个整数 x b

输出格式

输出用空格隔开的两个整数,表示 :��y:xb 化简后的前项与后项

样例

输入

3 3

Copy

输出

2 3

Copy

样例说明

33次方是27,前27个数中有18个数与27互质
1827 = 2 3

Limitation

1≤,≤1091x,b109

题思路

这个问题涉及到的关键概念是 Euler's Totient Function,记作 �(�)ϕ(n),表示小于等于 �n 的正整数中与 �n 互质的数的数量。

Euler's Totient Function �()(n) 的基本性质�ϕ

如果 �n 是一个正整数,�()(n) 的值可以通过 �ϕ�n 的素因子分解来计算: �()=(111)(112)...(11)(n)=n(1p11)(1p21)...(1pk1) 其中 ��−�−�−��ϕ−​−​−​�1,2,...,p1,p2,...,pk 是 ����n 的不同的素因子。

实现步骤

1=n1=xb: 需要高效计算幂,特别是当 计算 ����x 和 �b 较大时。

(1)(n1): 使用上述性质,找出 计算 ��ϕ�1n1 的所有素因子并计算。

: 化简 输出结果�(1):1(n1):n1 并输出其分子分母。��ϕ

注意事项

:由于 大数处理�x 和 �b 可以非常大,需要特别注意大数的处理和效率。

:使用欧几里得算法(GCD)来化简分数。化简分数

def gcd(a, b):

    while b:

        a, b = b, a % b

    return a

def compute_totient(n):

    result = n

    p = 2

    while p * p <= n:

        if n % p == 0:

            while n % p == 0:

                n //= p

            result -= result // p

        p += 1

    if n > 1:  # n is prime

        result -= result // n

    return result

def solve(x, b):

    # Compute n1 = x^b

    n1 = pow(x, b)

   

    # Compute phi(n1)

    n2 = compute_totient(n1)

   

    # Simplify the fraction n2 / n1

    g = gcd(n2, n1)

    numerator = n2 // g

    denominator = n1 // g

   

    # Print the result

    print(numerator, denominator)

# Example

solve(3, 3)

Problem 3D. Summation(equal version)

时间限制:1000ms

空间限制:256MB

题目描述

   给定一个长度为 �n,下标从 11开始的的序列 �a,请计算下列式子的值:
∑ =1=1(==)i=1∑n​j=1∑i​(ai​==aj​)
也就是说,你需要计算有多少对二元组( (,)(i,j)1≤)  1≤i≤j≤n满足�� =�ai​=aj​

输入格式

 第一行一个正整数,代表测试数据组数。�T

对于每一组有两行输入:

  第一行一个整数 �n,代表序列 �a的长度。

   第二行包含 �n个整数,用空格隔开,代表序列 �a �n个数。

输出格式

 输出 �T行,每行一个整数,代表该序列要计算式子的结果。

样例输入

 

3

 

4

 

1 1 1 1

 

3

 

1 2 3

 

5

 

1 2 1 2 1

Copy

样例输出

 

10

 

3

 

9

Copy

样例解释

 对于第三组数据,满足条件的二元组为, (1,1)(1,1), (1,3)(1,3), (1,5)(1,5), (2,2)(2,2), (2,4)(2,4), (3,3)(3,3), (3,5)(3,5), (4,4)(4,4)(5,5)(5,5)

数据规模与约定

 对于 60%60%的数据,1≤, 1031≤T≤1031≤,  1031≤n≤103 �n的总和不超过,103103

 对于 100%100%的数据,1≤, 1051≤T≤1051≤,   1051≤n≤105 �n的总和不超过, 105105序列中的每个数满足−109≤ 109−109≤ai​≤109

解题思路:


使用一个哈希表记录每个数字出现的频率。

  • 遍历哈希表,对于每个数字出现的频率,计算其对应的对数(即排列组合的方式),并累加到结果中。

    #include <iostream>

    #include <unordered_map>

    using namespace std;

    long long countPairs(const unordered_map<int, int>& freq) {

        long long total_pairs = 0;

        for (const auto& entry : freq) {

            long long f = entry.second;

            total_pairs += f * (f - 1) / 2; // 计算排列组合的方式

        }

        return total_pairs;

    }

    int main() {

        int T;

        cin >> T;

       

        for (int t = 0; t < T; ++t) {

            int n;

            cin >> n;

           

            unordered_map<int, int> freq;

            for (int i = 0; i < n; ++i) {

                int num;

                cin >> num;

                freq[num]++;

            }

           

            long long result = countPairs(freq);

            cout << result << endl;

        }

       

        return 0;

    }

    Problem 3E: K8s - Easy Version

    时间限制:2000ms

    空间限制:512MB

    题目描述

    电自院最近新设立了一台AI推理服务器,旨在预测校内各楼宇的耗电量。尽管这台AI服务器上装有多张显卡,但是由于负责该项目的同学,小F,并不会任何的后端技术,导致现在这台服务器只能串行处理请求,无法利用这么多显卡。由于小F的导师对这台服务器的QPS如此之低感到不满,一些改进方案被提了出来:

    AI服务器上现有个显卡,依次编号为�n0,1,2,...,,编号为。为充分利用这些显卡,小10,1,2,...,n−1的显卡算力为�i��ci​F为每个显卡都创建了一个Pod,每个Pod中都运行着调用某块显卡进行AI推理的程序。由于每个Pod中的显卡型号可能有差别,且任务的种类繁多,因此不同任务在不同Pod中的运行时间会有差异。经过研究统计,小F发现,AI服务器会收到种推理任务,编号为�m0,1,2,...,,类型编号为。当类型编号为10,1,2,...,m−1的任务的规模为�i��ai​的任务在编号为�i的显卡上运行时,所需要的时间为�j[毫秒。这里符号/][ai​/cj​][表示不大于][x]的最大整数。�x

    为了合理的将推理任务分配给Pod,小F设计了一种分配机制。每当AI服务器收到一个推理任务时,就会把这个任务分配给**响应用时最短**Pod;如果有多个Pod的响应用时均最短,那么分配给**完成任务的数量(含正在运行的任务)最少**Pod;如果有多个Pod的响应用时均最短且完成任务数量一致,那么分配给**编号最小**的那个Pod

    某时刻一个任务在某个Pod上的响应用时的定义为:将这一任务加入该Pod后,该Pod**开始**这一任务的时刻与当前时刻的时间差。例如,一个Pod正在运行一个需要10毫秒的任务,当前该任务已经运行了5毫秒,而且恰好还有3个用时分别为12毫秒、31毫秒和8毫秒的任务等待在该Pod上运行,如果有一个此时有一个新任务可能会分配到该Pod上,那么当前时刻这个新任务在该Pod上的响应时间就是毫秒。(10−5)+(12+31+8)=56(10−5)+(12+31+8)=56

    毫秒,类型编号为。这里保证所有现在,AI服务器收到了个推理任务,第�q个推理任务的到达服务器的时刻为�i��ti​��ki​��ti​严格递增,即对于∀。小{1,2,...,1},<+1∀i∈{1,2,...,q−1},ti​<ti+1F希望知道,按照上述方案,这些推理任务都会被分配给哪个Pod

    由于小F的代码水平稀烂,因此他无法完成这项工作。因此,他现在求助于你,希望你能帮他完成这个程序。

    输入格式

    1行,包含3个整数�;,,n,m,q

    2行,包含个整数,分别表示�n�;0,1,2,...,1c0​,c1​,c2​,...,cn1

    3行,包含个整数,分别表示�m�;0,1,2,...,1a0​,a1​,a2​,...,am1

    4~(行,每行+3)(q+3)2个整数,第(3+行的整数分别表示)(3+i)��,,�ti​,ki​

    一行内的多个整数之间使用1个空格分开,且每行的开头和末尾没有多余空格。

    输出格式

    输出行,每行�q1个整数。第行的整数表示服务器接收到的�i个推理任务被分配到的�iPod的显卡编号。

    样例输入
     2 3 5 
     6 8 
     190 328 112 
     0 1 
     12 0 
     38 0 
     39 2 
     204 1 

    Copy

    样例输出
     0 
     1 
     1 
     0 
     0 

    Copy

    样例解释

    本例中有2种显卡和3种任务。

    时间/ms

    任务类型编号

    显卡0任务数

    显卡0的等待队列

    显卡0的响应时间/ms

    显卡1任务数

    显卡1的等待队列

    显卡1的响应时间/ms

    选择哪个显卡?

    0

    1

    0

    0

    0

    0

    0号显卡

    12

    0

    1

    [54ms],首个任务已运行12ms

    54-12=42

    0

    0

    1号显卡

    38

    0

    1

    [54ms],首个任务已运行38ms

    54-38=16

    1

    0

    1号显卡

    39

    2

    1

    [54ms],首个任务已运行39ms

    54-39=15

    2

    [23ms],首个任务已运行1ms

    22

    0号显卡

    204

    1

    1

    0

    2

    0

    0号显卡

    数据范围及约定

    对于的数据,满足20%20%�,100,10n,q≤100,m≤10

    对于的数据,满足60%60%�;,,1000n,m,q≤1000

    对于的数据,100%100%1≤,,2×1051≤n,q≤2×1051≤,10001≤m≤10000≤,1090≤ti​≤1090≤,<0≤ki​<m1≤,所有输入数值均为非负整数。,1091≤ci​,ai​≤109

    提示

    猜猜这题为什么叫K8s?

    解题思路

    这个问题是一个任务调度问题,模拟在多个计算单元(Pods)上分配多个计算任务的情况。核心是对于每一个任务,我们需要实时地决定分配到哪一个Pod上,以确保最短的响应时间,如果响应时间相同,则需要考虑任务队列的长度和Pod的编号。

    实现步骤

    初始化

    对每个显卡(Pod)维护一个任务队列和一个总响应时间计数器。

    对于每个显卡(Pod),预先计算每种任务类型的处理时间并存储。

    处理每个任务

    对每个新到达的任务,更新每个Pod的当前时间(考虑之前的任务已完成的部分)。

    选择响应时间最短的Pod。如果有多个Pod响应时间相同,则选择任务队列长度最少的Pod。如果还是有多个,选择编号最小的。

    将任务添加到选定的Pod的队列中,并更新该Pod的响应时间和队列。

    输出结果

    对于每个任务,输出它被分配到的Pod的编号。

    数据结构

    使用一个列表来维护每个Pod的任务队列。

    使用一个列表来记录每个Pod的总响应时间。

    使用一个矩阵来存储每个Pod对每种任务类型的处理时间。

    性能注意事项

    每个任务的分配操作必须尽量优化,因为q的数量可以非常大(达到 2×1052×105)。

    预计算显卡的处理时间可以减少重复的计算,提高效率。

    def solve():

        import sys

        input = sys.stdin.read

        data = input().split()

       

        idx = 0

        n = int(data[idx])

        idx += 1

        m = int(data[idx])

        idx += 1

        q = int(data[idx])

        idx += 1

       

        c = list(map(int, data[idx:idx+n]))

        idx += n

        a = list(map(int, data[idx:idx+m]))

        idx += m

       

        # 预计算每个显卡对每种任务的处理时间

        task_times = [[(a[j] + c[i] - 1) // c[i] for j in range(m)] for i in range(n)]

       

        # 初始化每个Pod的状态

        pod_queues = [[0] for _ in range(n)]  # 每个Pod的任务队列(实际上存的是完成时刻)

        current_time = [0] * n  # 每个Pod的当前时间(考虑到达时间)

       

        results = []

        last_task_time = 0  # 上一个任务的到达时间

       

        tasks = [(int(data[idx + i * 2]), int(data[idx + i * 2 + 1])) for i in range(q)]

       

        for t, k in tasks:

            best_pod = 0

            min_response_time = float('inf')

            min_tasks_count = float('inf')

           

            # 计算每个Pod对当前任务的响应时间

            for i in range(n):

                # 当前Pod i, 处理任务k需要的时间

                process_time = task_times[i][k]

               

                # 更新Pod的当前时间,把已经完成的任务从队列中去除

                while pod_queues[i] and pod_queues[i][0] < t:

                    pod_queues[i].pop(0)

               

                # 此时Pod队列中的第一个任务是正在进行的任务(如果有)

                if pod_queues[i]:

                    response_time = pod_queues[i][-1] + process_time - t

                else:

                    response_time = process_time

               

                tasks_count = len(pod_queues[i])

               

                # 选择最佳Pod

                if (response_time < min_response_time or

                    (response_time == min_response_time and tasks_count < min_tasks_count) or

                    (response_time == min_response_time and tasks_count == min_tasks_count and i < best_pod)):

                    best_pod = i

                    min_response_time = response_time

                    min_tasks_count = tasks_count

           

            # 分配任务到最佳Pod

            if pod_queues[best_pod]:

                pod_queues[best_pod].append(pod_queues[best_pod][-1] + task_times[best_pod][k])

            else:

                pod_queues[best_pod].append(t + task_times[best_pod][k])

           

            results.append(best_pod)

       

        # 输出结果

        for result in results:

            print(result)

    Problem 3F. Time to empty

    Description

        在数集 �S中有在 11 �m之间的 �n个不同整数。从第0秒开始,你可以在每一秒进行以下操作:
  •    均匀随机选择�S中的一个元素�x
    •    �S中删除�x
      •  如果�  +1≤x+1≤m�                    +1x+1不在�S中,则将  �  +1x+1添加到�S中。

           请问在�S变为空集之前预期的秒数是多少? 输出答案对9+71e9+7取模。

                    正式地,让�  =1000000007P=1000000007。可以证明,答案可以表示为不可约分数��ba​,其中�a�b是整数,� ≢0(mod)b≡0(modP)。输出等于� 1moda⋅b−1modP的整数。换句话说,输出一个由0≤ <0≤z<P�  (mod)z⋅b≡a(modP)组成的整数�z

        Input Format

               第一行包含两个整数�n ( �m1≤ ) —— 5001≤n≤m≤500集合�S中的元素数和�S中元素值的上界。

             第二行包含�n个整数� ( 1,2,…,�S1​,S2​,…,Sn​1≤ ) —— 1<2<…<1≤S1​<S2​<…<Sn​≤m集合�S中的元素。

        Output Format

           输出一行一个整数,表示直到�S为空的期望秒数,对9+71e9+7取模。

        Test Case 1

        input
         2 3 
         1 3 

        Copy

        output
         750000009 

        Copy

        Test Case 2

        input
         5 10 
         1 2 3 4 5 

        Copy

        output
         300277731 

        Copy

        Test Case 3

        input
         5 10 
         2 3 6 8 9 

        Copy

        output
         695648216 

        Copy

        Note

        对于样例1,这里列出了所有可能的场景及其概率:

                1.       (50% [1,3][1,3]几率→→[1][1]→→[2][2]→→[3][3]→→[][]

          (50%      2.       (50% [1,3][1,3]几率→→[2,3][2,3]几率→→[2][2]→→[3][3]→→[][]

          (50%    3.       (50% [1,3][1,3]几率→→[2,3][2,3]几率→→[3][3]→→[][]

          把它们加起来,我们得到12 4+144+143=15421​⋅4+41​⋅4+41​⋅3=415​。而750000009  4≡15(mod1000000007)750000009⋅4≡15(mod1000000007)

        这个问题涉及概率,需要计算在每一步操作后,集合中元素的变化情况以及对应的概率。然后计算所有可能情况的期望值。下面是解题思路:

        维护一个数组,表示集合中元素为时,到达空集所需的期望秒数。dpdp[i]i

        从最大的元素开始向递推,计算每个的值。m1dp[i]

        对于,如果不在集合中,则;否则,。这里的表示选择的概率。dp[i]i+1dp[i]=dp[i+1]+1dp[i]=(dp[i+1]+1)*(n-i)/(m-i)(n-i)/(m-i)i+1

        最后输出即为所求结果。dp[1]

        根据取模运算的性质,可以在计算过程中适时对结果进行取模操作。

        #include <iostream>

        #include <vector>

        using namespace std;

        const int MOD = 1e9 + 7;

        int main() {

        int n, m;   

        cin >> n >> m;   

        vector<int> s(n);   

        for (int i = 0; i < n; ++i) {   

        cin >> s[i];       

        }   

        vector<long long> dp(m + 1);   

        for (int i = m - 1; i >= 1; --i) {   

        if (i < s[n - 1] || s.back() + 1 <= m) {       

        dp[i] = dp[i + 1] + 1;           

        } else {       

        long long prob = 1;           

        for (int j = n - 1; j >= 0 && s[j] >= i + 1; --j) {           

        prob = prob * (n - j) % MOD;               

        prob = prob * ((s[j] - i) * 1LL) % MOD;               

        prob = prob * ((MOD + 1) / 2) % MOD;               

        }           

        dp[i] = (dp[i + 1] + 1) * prob % MOD;           

        }       

        }   

        cout << dp[1] << endl;   

        return 0;   

        }






















































 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值