diff --git a/TangDou/Topic/PrefixAndSuffix/P3406.cpp b/TangDou/Topic/PrefixAndSuffix/P3406.cpp index b5ca5d1..32a332a 100644 --- a/TangDou/Topic/PrefixAndSuffix/P3406.cpp +++ b/TangDou/Topic/PrefixAndSuffix/P3406.cpp @@ -1,35 +1,35 @@ #include using namespace std; +const int N = 100005; #define int long long #define endl "\n" -const int N = 100010; -int n, m; // 铁路途经n个城市,要去m个城市 -int p[N]; // 记录经过站点的顺序 -int a[N], b[N], c[N]; // 记录每段路径所花的费用 -int t[N]; // 记录站点之间的路径经过的次数 -int ans; // 答案 +int p[N]; // 要访问的城市顺序 +int a[N]; // 买票 +int b[N]; // 充值 +int c[N]; // 买卡 +int q[N]; // 差分数组 +int res; + +// 数据范围是10^5*10^5,所以注意要开ll signed main() { + int n, m; cin >> n >> m; + for (int i = 0; i < m; i++) cin >> p[i]; // 记录访问顺序 + for (int i = 1; i <= n - 1; i++) cin >> a[i] >> b[i] >> c[i]; // 输入价格 - for (int i = 1; i <= m; i++) cin >> p[i]; - for (int i = 1; i < n; i++) cin >> a[i] >> b[i] >> c[i]; - - // 所有的区间都以较小的点排在前面,例如:2-1,5-3都用1-2,3-5表示, - // 且每一段都用前面较小的点作为标记!!!! - for (int i = 1; i < m; i++) { - int x, y; - if (p[i] > p[i + 1]) { - x = p[i + 1]; - y = p[i]; - } else { - x = p[i]; - y = p[i + 1]; - } - t[x]++; - t[y]--; + for (int i = 1; i < m; i++) { // 起点到终点,差分修改 + int t1 = p[i - 1]; + int t2 = p[i]; + if (t1 > t2) + q[t2] += 1, q[t1] -= 1; + else + q[t1] += 1, q[t2] -= 1; } - for (int i = 1; i <= n; i++) t[i] += t[i - 1]; // 求前缀和 - for (int i = 1; i <= n - 1; i++) - ans += min(a[i] * t[i], (b[i] * t[i] + c[i])); // 求总的最小就是把每一段的最小相加 - cout << ans << endl; + for (int i = 1; i <= n; i++) q[i] += q[i - 1]; // 前缀和处理 + + // 判断一下 n*a[i]和n*b[i]+c[i]的大小 + for (int i = 1; i < n; i++) + res += min(q[i] * a[i], q[i] * b[i] + c[i]); + + cout << res << endl; } \ No newline at end of file diff --git a/TangDou/Topic/【前缀和与差分】题单.md b/TangDou/Topic/【前缀和与差分】题单.md index 77fa6a0..6a760fd 100644 --- a/TangDou/Topic/【前缀和与差分】题单.md +++ b/TangDou/Topic/【前缀和与差分】题单.md @@ -165,22 +165,60 @@ signed main() { } ``` -### [$P3406$ 海底高铁](https://www.luogu.com.cn/problem/P3406) +#### [$P3406$ 海底高铁](https://www.luogu.com.cn/problem/P3406) +**题意** -**分析** -① 每一段的最小费用加起来则总体费用最小。 - -② 这里的区间是线段而不是一个具体的数,所以我们需要以一个统一的标准进行区间段的区分:于是我们想到了以每个区间的左端点值进行整个线段的记录。 +每一个条铁路都会被经过多次,求这一条铁路是办卡+充值花的钱少,还是直接买多次票花的钱少。 -③ 节约时间可以对某段区间做同样的加减数的方法:想到的就是差分(当然有差分就会有由差分求前缀和)。 - -④ 最后用得到的线段数比较两种购买方案。 +**分析** +我们其实只需要知道一条铁路被访问了几次就可以了,再通过比较 $c+b*n$ 和 $a$ 的值,就可以知道是买票好还是买卡好了。 +一个点到另外一个点的路径中,中间经过的铁路都会被访问一遍,如果每次都一个一个的加这样太慢了,会$TLE$ -**注意** -当然代码中还有很多需要记录的细节!!!例如:线段数是站点数-1,同时差分和前缀和的循环最好是从1开始(涉及边界问题) +发现这些铁路实质上是一维的,并且是关于区间的修改的,那么就可以用差分优化了 +```cpp {.line-numbers} +#include +#include +using namespace std; +const int N = 100005; +typedef long long ll; +int p[N]; //要访问的城市顺序 +ll a[N]; //买票 +ll b[N]; //充值 +ll c[N]; //买卡 +ll q[N]; //差分数组 +//数据范围是10^5*10^5,所以注意要开ll +int main() +{ + int n, m; + cin >> n >> m; + for (int i = 0;i < m;i++) + cin >> p[i]; //记录访问顺序 + for (int i = 1;i <= n - 1;i++) //输入价格 + cin >> a[i] >> b[i] >> c[i]; + ll res = 0; + for (int i = 1;i < m ;i++) + {//起点到终点,差分修改 + int t1 = p[i - 1]; + int t2 = p[i]; + if (t1 > t2) + q[t2] += 1, q[t1] -= 1; + else + q[t1] += 1, q[t2] -= 1; + } + for (int i = 1;i <=n;i++) + {//前缀和处理 + q[i] += q[i - 1]; + } + for (int i = 1;i <= n-1;i++) + {//判断一下 n*a[i]和n*b[i]+c[i]的大小 + res += min(q[i] * a[i], q[i] * b[i] + c[i]); + } + cout << res; +} +``` #### [$P1719$ 最大加权矩阵](https://www.luogu.com.cn/problem/P3406) #### [$P2004$ 领地选择](https://www.luogu.com.cn/problem/P2004)