洛谷 P3406 海底高铁
题目:https://www.luogu.com.cn/problem/P3406
关于前缀和与差分,你可能需要了解:前缀和与差分
解题思路:结构体、前缀和、差分、贪心
-
思路(写在前面):看每一站经过的次数,当前次数下那种方式更加优惠,可以花费更加少的钱💴(注意数据范围,longlong否则会卡后三个四个测试点)
-
结构体:因为在一个状态(第 i 个站台的时候,存在一些元素需要存储)这时候可以使用结构体进行打包存储【plan:计划执行那种计算。Ai、Bi、Ci:直接买票、优惠票、优惠卡 价格】
- 一个函数用于计算经过一个站,x次的情况下,plan执行直接买票,还是执行买 优惠卡+优惠票组合更加合适;
struct price { bool plan; int Ai, Bi, Ci; }p[100005]; bool plan_A_or_BC(int i) { return p[i].Ai * a[i] <= p[i].Ci + p[i].Bi * a[i]; }
-
贪心:先找到个站都经过了几次,然后和当前站的Ai、Bi、Ci做计算得出结果,比较大小,找到当前站点的最小价格,然后每个站点都找到最小价格。和就是总的最小价格
if (p[i].plan) count += p[i].Ai * a[i]; else count += p[i].Ci + p[i].Bi * a[i];
-
差分:通过输入的M个元素,找到相邻站点之间经过了那些站点,进行标记这里有一个惯性思维错误:使用 [start] + 1 & [end + 1] - 1进行 diff 预处理,这是错误的,题目要求是站点 i 的时候表示区间是 (i, i + 1);意思就是说:如果我们从站点 1 开到 站点 3 那么我们实际经过的 站点 是 1 → 2、 2 → 3;而不是1、2、3所以我们在进行预处理的时候在 3 的位置(也就是 end 的位置)就应该 -1 了※※
int pre; cin >> pre; m--; while(m--) { int now; cin >> now; pre <= now ? diff[pre] += 1 : diff[now] += 1; pre <= now ? diff[now] -= 1 : diff[pre] -= 1; pre = now; }
-
前缀和:差分的前缀和就是元素组元素,我们可以通过差分前缀和的方式找到数据源,然后进行贪心
a[i] = a[i - 1] + diff[i];
完整代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
struct price
{
bool plan;
int Ai, Bi, Ci;
}p[100005];
ll a[100005];
bool plan_A_or_BC(int i)
{
return p[i].Ai * a[i] <= p[i].Ci + p[i].Bi * a[i];
}
int main()
{
int n, m; cin >> n >> m;
int diff[100005] = {0};
int pre; cin >> pre; m--;
while(m--)
{
int now; cin >> now;
pre <= now ? diff[pre] += 1 : diff[now] += 1;
pre <= now ? diff[now] -= 1 : diff[pre] -= 1;
pre = now;
}
for (int i = 1; i <= n; i++)
{
cin >> p[i].Ai >> p[i].Bi >> p[i].Ci;
a[i] = a[i - 1] + diff[i];
p[i].plan = plan_A_or_BC(i);
}
ll count = 0;
for (int i = 1; i <= n; i++)
{
if (p[i].plan) count += p[i].Ai * a[i];
else count += p[i].Ci + p[i].Bi * a[i];
}
cout << count;
return 0;
}