AtCoder Weekday Contest 0021 Beta题解(AWC 0021 Beta A-E)

A - Counting the Number of Successful Applicants

【题目来源】

AtCoder:A - Counting the Number of Successful Applicants

【题目描述】

Takahashi is a lecturer at a cram school. At this school, students from \(N\) classes took a mock exam.
高桥是补习班的讲师。在这所学校,有 \(N\) 个班级的学生参加了一场模拟考试。

Each class \(i\) (\(1 \leq i \leq N\)) has \(A_i\) students, and each student's exam score has been recorded. The score of the \(j\)-th student (\(1 \leq j \leq A_i\)) in class \(i\) is \(B_{i,j}\) points.
每个班级 \(i\)\(1 \leq i \leq N\))有 \(A_i\) 名学生,每位学生的考试成绩已被记录。班级 \(i\) 中第 \(j\) 名学生(\(1 \leq j \leq A_i\))的分数为 \(B_{i,j}\) 分。

Takahashi set a passing score of \(K\) points as the passing criterion. A student whose score is \(K\) points or higher is judged as passing.
高桥设定了 \(K\) 分作为及格的分数线。分数不低于 \(K\) 分的学生被判定为通过。

Find the total number of students judged as passing across all classes.
求所有班级中被判定为通过的学生总数。

【输入】

\(N\) \(K\)
\(A_1\) \(B_{1,1}\) \(B_{1,2}\) \(\ldots\) \(B_{1,A_1}\)
\(A_2\) \(B_{2,1}\) \(B_{2,2}\) \(\ldots\) \(B_{2,A_2}\)
\(\vdots\)
\(A_N\) \(B_{N,1}\) \(B_{N,2}\) \(\ldots\) \(B_{N,A_N}\)

  • The first line contains the number of classes \(N\) and the passing score \(K\), separated by a space.
  • The \((i + 1)\)-th line (\(1 \leq i \leq N\)) contains the number of students \(A_i\) in class \(i\), followed by the scores \(B_{i,1}, B_{i,2}, \ldots, B_{i,A_i}\) of each student in that class, separated by spaces.

【输出】

Print the total number of students whose score is \(K\) points or higher, on a single line.

【输入样例】

3 60
4 45 72 60 88
3 59 60 61
2 100 30

【输出样例】

6

【解题思路】

image

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int n, k, ans;  // n: 组数,k: 阈值,ans: 计数结果

int main()
{
    cin >> n >> k;  // 读入组数和阈值
    
    for (int i = 1; i <= n; i++)  // 遍历每组
    {
        int m;  // 当前组中的数字个数
        cin >> m;  // 读入当前组的数字个数
        
        for (int j = 1; j <= m; j++)  // 遍历当前组中的每个数字
        {
            int b; 
            cin >> b;  // 读入数字
            
            if (b >= k)  // 如果数字大于等于阈值k
            {
                ans++;  // 计数加1
            }
        }
    }
    
    cout << ans << endl;  // 输出大于等于k的数字总数
    return 0;
}

【运行结果】

3 60
4 45 72 60 88
3 59 60 61
2 100 30
6

B - Scholarship Selection

【题目来源】

AtCoder:B - Scholarship Selection

【题目描述】

Takahashi is a member of the university's scholarship selection committee. This year, there are \(N\) types of scholarships, and for each one, a recipient must be determined from among the students who applied.
高桥是大学奖学金评选委员会的成员。今年有 \(N\) 种奖学金,每种奖学金必须从申请的学生中确定一名获得者。

The university has \(M\) applicants, and applicant \(j\) \((1 \leq j \leq M)\) has been assigned an evaluation score \(P_j\) based on their academic performance. The evaluation score is a fixed value for each applicant, and a higher value means a higher evaluation. It is possible for different applicants to have the same evaluation score.
大学有 \(M\) 名申请者,申请者 \(j\)\(1 \leq j \leq M\))根据其学业表现被评定了一个评分 \(P_j\)。评分是每位申请者的固定值,数值越高表示评价越高。不同的申请者可能有相同的评分。

A single applicant may apply for multiple scholarships. Since the recipient of each scholarship is determined independently, a single applicant may receive multiple scholarships.
一位申请者可以申请多项奖学金。由于每项奖学金的获得者是独立确定的,一位申请者可能获得多项奖学金。

For each scholarship \(i\) \((1 \leq i \leq N)\), the applicant numbers \(C_{i,1}, C_{i,2}, \ldots, C_{i,K_i}\) of the \(K_i\) applicants who applied for that scholarship are given. The recipient is determined by the following rules:
对于每项奖学金 \(i\)\(1 \leq i \leq N\)),给出了申请该奖学金的 \(K_i\) 名申请者的申请编号 \(C_{i,1}, C_{i,2}, \ldots, C_{i,K_i}\)。获得者按以下规则确定:

  • If there are no applicants (\(K_i = 0\)), there is no recipient for that scholarship.
    如果没有申请者(\(K_i = 0\)),则该奖学金没有获得者。
  • If there are applicants, the applicant with the highest evaluation score receives the scholarship. If there are multiple applicants with the highest evaluation score, the one with the smallest applicant number among them receives it.
    如果有申请者,则评分最高的申请者获得该奖学金。如果有多个申请者评分相同且最高,则其中申请编号最小者获得。

For each scholarship, determine the applicant number of the person who ultimately receives that scholarship. If there is no recipient, output \(0\).
对于每项奖学金,确定最终获得该奖学金的申请者编号。如果没有获得者,则输出 \(0\)

【输入】

\(N\) \(M\)
\(P_1\) \(P_2\) \(\ldots\) \(P_M\)
\(K_1\) \(C_{1,1}\) \(C_{1,2}\) \(\ldots\) \(C_{1,K_1}\)
\(K_2\) \(C_{2,1}\) \(C_{2,2}\) \(\ldots\) \(C_{2,K_2}\)
\(\vdots\)
\(K_N\) \(C_{N,1}\) \(C_{N,2}\) \(\ldots\) \(C_{N,K_N}\)

  • The first line contains an integer \(N\) representing the number of types of scholarships and an integer \(M\) representing the number of applicants, separated by a space.
  • The second line contains integers \(P_1, P_2, \ldots, P_M\) representing the evaluation scores of each applicant, separated by spaces. \(P_j\) represents the evaluation score of applicant \(j\).
  • The \((2 + i)\)-th line \((1 \leq i \leq N)\) contains information about the students who applied for scholarship \(i\). First, an integer \(K_i\) representing the number of applicants is given, followed by the integers \(C_{i,1}, C_{i,2}, \ldots, C_{i,K_i}\) representing the applicant numbers, separated by spaces. If \(K_i = 0\), only \(K_i\) is given on that line.

【输出】

Output \(N\) lines. The \(i\)-th line \((1 \leq i \leq N)\) should contain the applicant number of the person who receives scholarship \(i\). If there is no recipient, output \(0\).

【输入样例】

3 4
100 80 100 90
2 1 2
3 2 3 4
0

【输出样例】

1
3
0

【解题思路】

image

【代码详解】

#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
int n, m, a[N], c[N];  // n: 询问次数,m: 物品种类数,a[i]: 第i种物品的评分,c[]: 临时存储选择的物品编号

int main()
{
    cin >> n >> m;  // 读入询问次数和物品种类数
    
    for (int i = 1; i <= m; i++)
    {
        cin >> a[i];  // 读入每种物品的评分
    }
    
    for (int i = 1; i <= n; i++)  // 处理每个询问
    {
        int k; 
        cin >> k;  // 读入当前询问选择的物品数量
        
        if (k == 0)  // 如果没有选择任何物品
        {
            cout << 0 << endl;  // 输出0
            continue;  // 继续下一个询问
        }
        
        int c; 
        cin >> c;  // 读入第一个物品编号
        int maxn = a[c];  // 当前最大评分
        int maxid = c;  // 当前评分最大的物品编号
        
        for (int j = 2; j <= k; j++)  // 处理剩余的k-1个物品
        {
            cin >> c;  // 读入物品编号
            
            // 如果当前物品评分更高,或者评分相同但编号更小
            if (a[c] > maxn || (a[c] == maxn && c < maxid))
            {
                maxn = a[c];  // 更新最大评分
                maxid = c;  // 更新最大评分物品编号
            }
        }
        
        cout << maxid << endl;  // 输出评分最大的物品编号
    }
    
    return 0;
}

【运行结果】

3 4
100 80 100 90
2 1 2
1
3 2 3 4
3
0
0

C - Bargain Shopping Mall Tour

【题目来源】

AtCoder:C - Bargain Shopping Mall Tour

【题目描述】

Takahashi is planning to visit \(N\) shopping malls in the suburbs to shop during a weekend sale.
高桥计划在周末促销期间,去郊区的 \(N\) 个购物中心购物。

Each shopping mall \(i\) (\(1 \leq i \leq N\)) has \(M_i\) stores, each selling sale items. If Takahashi shops at the \(j\)-th store (\(1 \leq j \leq M_i\)) of shopping mall \(i\), he can gain a benefit value of \(P_{i,j}\) yen.
每个购物中心 \(i\)\(1 \leq i \leq N\))有 \(M_i\) 家商店,每家都在销售促销商品。如果高桥在购物中心 \(i\) 的第 \(j\) 家商店(\(1 \leq j \leq M_i\))购物,他可以获得 \(P_{i,j}\) 日元的收益值。

To visit shopping mall \(i\), he must pay a visiting cost of \(C_i\) yen. This cost is a fixed value determined for each mall.
要访问购物中心 \(i\),他必须支付 \(C_i\) 日元的访问成本。这个成本是每个购物中心确定的固定值。

Due to time constraints, Takahashi can choose and visit at most \(K\) malls out of the \(N\) malls (it is also possible to visit none). He cannot visit the same mall more than once.
由于时间限制,高桥最多可以从 \(N\) 个购物中心中选择并访问 \(K\) 个(也可以不访问任何购物中心)。他不能多次访问同一个购物中心。

At each mall he visits, he can choose any number (zero or more) of stores from the \(M_i\) stores in that mall to shop at. He can shop at each store at most once. Note that even if he does not choose any store at a mall he visits, he still must pay the visiting cost \(C_i\).
在他访问的每个购物中心,他可以从该购物中心的 \(M_i\) 家商店中选择任意数量(零家或更多家)的商店进行购物。每家商店他最多只能购物一次。注意,即使他在访问的购物中心没有选择任何商店,他仍需支付访问成本 \(C_i\)

Maximize the total profit that Takahashi can obtain. The total profit is defined as follows:
最大化高桥可以获得的总利润。总利润定义如下:

\(\text{Total profit} = \sum_{\text{stores } (i,j) \text{ where he shopped}} P_{i,j} - \sum_{\text{malls } i \text{ he visited}} C_i\)

In other words, it is the sum of benefit values of all stores where he shopped, minus the sum of visiting costs of all malls he visited. If he visits no malls, the total profit is \(0\). Therefore, the maximum total profit is at least \(0\).
换句话说,就是他购物的所有商店的收益值之和,减去他访问的所有购物中心的访问成本之和。如果他不访问任何购物中心,总利润为 \(0\)。因此,最大总利润至少为 \(0\)

【输入】

\(N\) \(K\)
\(C_1\) \(M_1\) \(P_{1,1}\) \(P_{1,2}\) \(\ldots\) \(P_{1,M_1}\)
\(C_2\) \(M_2\) \(P_{2,1}\) \(P_{2,2}\) \(\ldots\) \(P_{2,M_2}\)
\(\vdots\)
\(C_N\) \(M_N\) \(P_{N,1}\) \(P_{N,2}\) \(\ldots\) \(P_{N,M_N}\)

  • The first line contains the number of shopping malls \(N\) and the maximum number of malls that can be visited \(K\), separated by a space.
  • In the following \(N\) lines, the \(i\)-th line (\(1 \leq i \leq N\)) contains the visiting cost \(C_i\) of mall \(i\), the number of stores \(M_i\), and the benefit values \(P_{i,1}, P_{i,2}, \ldots, P_{i,M_i}\) of each store, separated by spaces (consisting of \(2 + M_i\) values).

【输出】

Print the maximum total profit that Takahashi can obtain, in a single line.

【输入样例】

3 2
500 3 200 300 100
300 2 400 150
800 4 250 250 250 250

【输出样例】

450

【解题思路】

image

【代码详解】

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 100005;
int n, k, cur, ans;  // n: 总商品数,k: 最多可购买的商品数,cur: 有效商品数,ans: 最大收益
struct Node
{
    int c, p;  // c: 成本,p: 总收入
}a[N];  // 存储有效商品

// 比较函数:按利润(p-c)从大到小排序
bool cmp(Node x, Node y)
{
    return x.p - x.c > y.p - y.c;
}

signed main()
{
    cin >> n >> k;  // 读入总商品数和最多可购买的商品数
    
    for (int i = 1; i <= n; i++)  // 遍历每个商品
    {
        int c, m;  // c: 成本,m: 客户数量
        cin >> c >> m;  // 读入成本和客户数
        
        int tot = 0;  // 计算总收入
        for (int j = 1; j <= m; j++)  // 遍历每个客户
        {
            int p;  // 客户支付的价格
            cin >> p;
            tot += p;  // 累加总收入
        }
        
        if (tot - c > 0)  // 如果商品有正利润
        {
            a[++cur] = (Node){c, tot};  // 将商品加入有效商品列表
        }
    }
    
    // 按利润从大到小排序
    sort(a + 1, a + cur + 1, cmp);
    
    // 选择前k个利润最大的商品
    for (int i = 1; i <= k; i++)
    {
        ans += (a[i].p - a[i].c);  // 累加利润
    }
    
    cout << ans << endl;  // 输出最大总收益
    return 0;
}

【运行结果】

3 2
500 3 200 300 100
300 2 400 150
800 4 250 250 250 250
450

D - Checkpoint Rally

【题目来源】

AtCoder:D - Checkpoint Rally

【题目描述】

Takahashi is participating in an orienteering competition. In this competition, he must visit designated checkpoints in order and reach the goal.
高桥正在参加一场定向越野比赛。在这项比赛中,他必须按顺序访问指定的检查点,并到达终点。

The competition venue has \(N\) locations, numbered from \(1\) to \(N\). There are \(M\) bidirectional roads between these locations. The \(i\)-th road \((1 \leq i \leq M)\) connects location \(U_i\) and location \(V_i\), and takes \(T_i\) minutes to traverse in either direction. There may be multiple roads connecting the same pair of locations. Also, it is not guaranteed that all locations are reachable from each other via roads.
比赛场地有 \(N\) 个地点,编号从 \(1\)\(N\)。这些地点之间有 \(M\) 条双向道路。第 \(i\) 条道路(\(1 \leq i \leq M\))连接地点 \(U_i\) 和地点 \(V_i\),无论沿哪个方向通行都需要 \(T_i\) 分钟。同一对地点之间可能有多条道路连接。此外,不能保证所有地点都可以通过道路互相到达。

Takahashi is currently at location \(1\), the starting point. According to the competition rules, he must visit \(K\) designated checkpoints \(P_1, P_2, \ldots, P_K\) in this exact order, and finally arrive at location \(N\), the goal. Specifically, he departs from location \(1\) and reaches location \(P_1\), then reaches location \(P_2\), \(\ldots\), reaches location \(P_K\), and finally arrives at location \(N\).
高桥目前位于起点地点 \(1\)。根据比赛规则,他必须按照这个确切顺序访问 \(K\) 个指定检查点 \(P_1, P_2, \ldots, P_K\),最后到达终点地点 \(N\)。具体来说,他从地点 \(1\) 出发,到达地点 \(P_1\),然后到达地点 \(P_2\),……,到达地点 \(P_K\),最后到达地点 \(N\)

The rules regarding checkpoint visits are as follows:
关于检查点访问的规则如下:

  • Checkpoints are completed only in the order \(P_1, P_2, \ldots, P_K\). When arriving at a location, if that location is the next checkpoint to be visited, the visit to that checkpoint is completed immediately. No time other than travel time is incurred.
    检查点只能按照 \(P_1, P_2, \ldots, P_K\) 的顺序完成。当到达一个地点时,如果该地点是下一个要访问的检查点,则立即完成对该检查点的访问。除了旅行时间外,不产生其他时间。
  • Since Takahashi starts at location \(1\), if \(P_1 = 1\), the visit to checkpoint \(P_1\) is completed at the moment of departure. In general, when a checkpoint visit is completed, if the next checkpoint to be visited is the same as the current location, that checkpoint's visit is also completed immediately.
    由于高桥从地点 \(1\) 开始,如果 \(P_1 = 1\),则在出发时即完成对检查点 \(P_1\) 的访问。一般来说,当一个检查点访问完成时,如果下一个要访问的检查点与当前位置相同,则该检查点的访问也立即完成。
  • Even if you pass through a location of a checkpoint whose turn has not yet come, that checkpoint is not completed. You must either already be at that location when its turn comes, or revisit it.
    即使你经过了一个尚未轮到访问的检查点所在地点,该检查点也不会被完成。你必须在轮到时已经位于该地点,或重新访问它。
  • The designated checkpoint numbers are not necessarily distinct from each other, and may coincide with location \(1\) or location \(N\).
    指定的检查点编号不一定互不相同,并且可能与地点 \(1\) 或地点 \(N\) 重合。

Throughout the entire journey, you may pass through the same location or the same road any number of times.
在整个旅程中,你可以多次经过同一地点或同一条道路。

Since Takahashi aims to win, he wants to reach the goal in the shortest possible time. Find the minimum total travel time (in minutes) to depart from location \(1\), visit \(P_1, P_2, \ldots, P_K\) in order, and then arrive at location \(N\). If no valid route exists, output -1.
由于高桥志在取胜,他希望以最短的可能时间到达终点。求从地点 \(1\) 出发,按顺序访问 \(P_1, P_2, \ldots, P_K\),然后到达地点 \(N\) 的最小总旅行时间(以分钟为单位)。如果没有有效的路线,输出 -1

【输入】

\(N\) \(M\) \(K\)
\(U_1\) \(V_1\) \(T_1\)
\(U_2\) \(V_2\) \(T_2\)
\(\vdots\)
\(U_M\) \(V_M\) \(T_M\)
\(P_1\) \(P_2\) \(\ldots\) \(P_K\)

  • The first line contains the number of locations \(N\), the number of roads \(M\), and the number of checkpoints to visit \(K\), separated by spaces.
  • The following \(M\) lines each contain integers \(U_i, V_i, T_i\) separated by spaces, indicating that the \(i\)-th road \((1 \leq i \leq M)\) bidirectionally connects location \(U_i\) and location \(V_i\) and takes \(T_i\) minutes to traverse.
  • The last line contains the \(K\) checkpoint numbers \(P_1, P_2, \ldots, P_K\) separated by spaces. They must be visited in this order.

【输出】

Output in a single line the minimum total travel time (in minutes) as an integer to depart from location \(1\), visit \(P_1, P_2, \ldots, P_K\) in order, and arrive at location \(N\). If no valid route exists, output -1.

Note that the answer may exceed \(10^{10}\).

【输入样例】

5 6 2
1 2 3
2 3 4
3 5 2
1 4 10
4 5 1
2 4 5
3 4

【输出样例】

11

【解题思路】

image

【代码详解】

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 50005, M = 100005, INF = 1e18;
// 求1号点到n号点的最短距离,如果不存在,则返回-1
// 链式前向星
int n, m, k, p[N], ans;  // n: 点数,m: 边数,k: 特殊点数(不包含起点和终点),p: 特殊点序列,ans: 总路径长度
int h[N], w[M * 2], e[M * 2], ne[M * 2], idx;  // 邻接表存储图
int dist[N];  // 存储从源点到所有点的最短距离
bool st[N];  // 标记节点是否已确定最短距离
typedef pair<int, int> PII;
priority_queue<PII, vector<PII>, greater<PII> > heap;  // 最小堆,存储(距离, 节点)

// 添加一条从a到b,边权为c的无向边
void add(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}

// Dijkstra算法,计算从源点u到所有点的最短距离
void dijkstra(int u)
{
    // 初始化距离数组
    for (int i = 1; i <= n; i++)
    {
        dist[i] = INF;  // 初始化为无穷大
    }
    memset(st, 0, sizeof(st));  // 重置标记数组
    while (!heap.empty())  // 清空优先队列
    {
        heap.pop();
    }
    
    dist[u] = 0;  // 源点距离为0
    heap.push({0, u});  // 将源点加入堆
    
    while (!heap.empty())
    {
        auto t = heap.top(); 
        heap.pop();
        int veid = t.second, distance = t.first;  // 当前最短距离节点
        
        if (st[veid] == true)  // 如果已确定最短距离,跳过
        {
            continue;
        }
        st[veid] = true;  // 标记为已确定
        
        // 遍历当前节点的所有邻接点
        for (int i = h[veid]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > distance + w[i])  // 如果能通过当前节点更新到j的距离
            {
                dist[j] = distance + w[i];  // 更新距离
                heap.push({dist[j], j});  // 加入堆
            }
        }
    }
}

signed main()
{
    cin >> n >> m >> k;  // 读入点数、边数、特殊点数
    memset(h, -1, sizeof(h));  // 初始化邻接表
    
    // 读入无向图的边
    for (int i = 1; i <= m; i++)
    {
        int u, v, t;
        cin >> u >> v >> t;
        add(u, v, t), add(v, u, t);  // 无向边,添加两次
    }
    
    // 读入k个特殊点
    for (int i = 1; i <= k; i++)
    {
        cin >> p[i];
    }
    
    p[0] = 1;  // 起点设为1
    p[++k] = n;  // 终点设为n,现在p[0..k]表示完整的路径序列
    
    // 计算从p[0]到p[1], p[1]到p[2], ..., p[k-1]到p[k]的最短距离
    for (int i = 0; i < k; i++)
    {
        if (p[i] == p[i + 1])  // 如果起点和终点相同,跳过
        {
            continue;
        }
        
        dijkstra(p[i]);  // 从p[i]出发求最短路
        
        if (dist[p[i + 1]] >= INF / 2)  // 如果不可达
        {
            cout << -1 << endl;
            return 0;
        }
        
        ans += dist[p[i + 1]];  // 累加路径长度
    }
    
    cout << ans << endl;  // 输出总路径长度
    return 0;
}

【运行结果】

5 6 2
1 2 3
2 3 4
3 5 2
1 4 10
4 5 1
2 4 5
3 4
11

E - Field Watering Plan

【题目来源】

AtCoder:E - Field Watering Plan

【题目描述】

Takahashi will water his field over \(N\) days. Each day, he chooses and executes exactly one of two watering methods.|
高桥将在 \(N\) 天内灌溉他的田地。每一天,他都会从两种灌溉方法中选择并执行其中一种。

Method A is a method that spreads a large amount of water all at once. On a day when this method is chosen, it basically yields a growth amount of \(a\), but because too much water is spread, the soil becomes waterlogged, and the growth amount obtained on the next day is halved (there is no effect on days after that).
方法 A 是一种一次性大量浇水的方法。在选择此方法的那一天,基本可以获得 \(a\) 的生长量,但由于浇水过多,土壤会积水,导致下一天获得的生长量减半(对之后的天数没有影响)。

Method B is a method that carefully spreads a small amount of water. On a day when this method is chosen, it basically yields a growth amount of \(b\). Since there is no burden on the soil, there is no negative effect on the next day.
方法 B 是一种少量谨慎浇水的方法。在选择此方法的那一天,基本可以获得 \(b\) 的生长量。由于对土壤没有负担,因此对下一天没有负面影响。

Specifically, the growth amount for each day is determined by the following rules:
具体来说,每天的生长量由以下规则决定:

  • If Method B was used on the previous day, or if it is the first day (no previous day exists), no halving occurs. If Method A is chosen, the growth amount is \(a\); if Method B is chosen, the growth amount is \(b\).
    如果前一天使用了方法 B,或者这是第一天(没有前一天),则不减半。如果选择方法 A,生长量为 \(a\);如果选择方法 B,生长量为 \(b\)
  • If Method A was used on the previous day, the growth amount for that day is halved. If Method A is chosen, the growth amount is \(\lfloor a / 2 \rfloor\); if Method B is chosen, the growth amount is \(\lfloor b / 2 \rfloor\). Here, \(\lfloor x \rfloor\) denotes the largest integer not exceeding \(x\).
    如果前一天使用了方法 A,则当天的生长量减半。如果选择方法 A,生长量为 \(\lfloor a / 2 \rfloor\);如果选择方法 B,生长量为 \(\lfloor b / 2 \rfloor\)。这里,\(\lfloor x \rfloor\) 表示不超过 \(x\) 的最大整数。

The halving is always applied to the base values \(a\) or \(b\). It does not matter whether the previous day's growth amount was itself halved; the determination is based solely on "whether Method A was used on the previous day." For example, if Method A is chosen for three consecutive days, the growth amounts are \(a\) on the first day, \(\lfloor a/2 \rfloor\) on the second day, and \(\lfloor a/2 \rfloor\) on the third day.
减半始终适用于基准值 \(a\)\(b\)。前一天的生长量是否本身已被减半并不重要;判断仅基于"前一天是否使用了方法 A"。例如,如果连续三天选择方法 A,则第一天的生长量为 \(a\),第二天的生长量为 \(\lfloor a/2 \rfloor\),第三天的生长量为 \(\lfloor a/2 \rfloor\)

Find the maximum possible total growth amount when the watering methods over the \(N\) days are chosen optimally.
求在 \(N\) 天内最优选择灌溉方法时,可能获得的最大总生长量。

【输入】

\(N\) \(a\) \(b\)

  • The first line contains the number of days \(N\), the base growth amount of Method A \(a\), and the base growth amount of Method B \(b\), separated by spaces.

【输出】

Output the maximum possible total growth amount in a single line.

【输入样例】

5 10 3

【输出样例】

32

【解题思路】

image

【代码详解】

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int INF = 1e18;
int n, a, b, ans = -INF;  // n: 序列长度,a,b: 参数,ans: 最终答案

// 定义2x2矩阵结构体
struct matrix
{
    int c[2][2];  // 2x2矩阵
    matrix()  // 构造函数,初始化为0
    {
        memset(c, 0, sizeof(c));
    }
};

// 矩阵乘法重载,但使用max和+代替乘法和加法
matrix operator*(matrix &x, matrix &y)
{
    matrix t;  // 结果矩阵
    for (int i = 0; i < 2; i++)  // 行
    {
        for (int j = 0; j < 2; j++)  // 列
        {
            for (int k = 0; k < 2; k++)  // 中间维度
            {
                // 特殊的"矩阵乘法":t[i][j] = max(t[i][j], x[i][k] + y[k][j])
                t.c[i][j] = max(t.c[i][j], x.c[i][k] + y.c[k][j]);
            }
        }
    }
    return t;
}

// 矩阵快速幂
matrix qmi(matrix a, int k)
{
    matrix res;  // 单位矩阵(这里初始化为全0,相当于加法的单位元)
    while (k)
    {
        if (k & 1)  // 如果当前二进制位为1
        {
            res = res * a;  // 乘以当前矩阵
        }
        a = a * a;  // 矩阵平方
        k >>= 1;  // 右移一位
    }
    return res;
}

signed main()
{
    cin >> n >> a >> b;  // 读入序列长度和参数a,b
    
    matrix M;  // 转移矩阵
    // 设置转移矩阵的值
    M.c[0][0] = a / 2;  // 状态0到状态0的转移值
    M.c[0][1] = b / 2;  // 状态0到状态1的转移值
    M.c[1][0] = a;      // 状态1到状态0的转移值
    M.c[1][1] = b;      // 状态1到状态1的转移值
    
    matrix Mn = qmi(M, n);  // 计算M的n次幂
    
    // 取结果矩阵中的最大值作为答案
    ans = max({Mn.c[0][0], Mn.c[0][1], Mn.c[1][0], Mn.c[1][1]});
    
    cout << ans << endl;  // 输出结果
    return 0;
}

【运行结果】

5 10 3
32
posted @ 2026-03-12 22:15  团爸讲算法  阅读(2)  评论(0)    收藏  举报