水站
阿新 • • 發佈:2020-07-16
題目大意
- 已知有一個n層的水站:
- Wi表示未操作之前第i層的已有水量;
- Li表示第i個水站能夠維持或者儲存的水的重量;
- Pi表示在第i層進行減壓放水操作所需的費用.
- 被壓減放水層所儲存的所有水都將流向下一層。如果第i層的水量比Li大,則這一層也會(自動)減壓(不需要任何費用)。
- 現在想要使最後一層減壓(第n級),求最少的花費。這個任務現在交給了你。
輸入格式
- 每個輸入的第一行包含一個自然數n(1 =< n <= 150000)
- 接下來n行每行包含3個數Wi,Li,Pi。
輸出格式
- 第一行輸出所需的最小費用
- 第二行若干個整數,從小到大輸出必須減壓的層的編號。
樣例
樣例輸入
3
1000 1000 1
0 1000 2
2 10 100
樣例輸出
3
1 2
演算法分析
- 模擬很好想吧 首先先來看一個事實: 如果當前節點i是第一個花錢把它減壓的 那麼從i到n一定所有的節點都減壓了 因為如果在中間的某個節點沒有減壓的話 那麼它前面的減壓花費相當於白給
- 但是模擬會T 所以我們要準備想正解了
- 如果假設當前節點為k 在節點k之前有一個節點i 如果從i之前開始那麼k不需要主動減壓 從i之後開始需要主動減壓(i越靠前到k處的水就越多 很容易 自己想)
- 而關於這個節點的位置我們可以二分查詢
- 然後我們定義 ci 為從第 i 層開始放水一直放到第 n 層所需的費用. 考慮第 k 層的強制放水花費會對整個 ⟨ci⟩ 產生什麼樣的影響. 顯然有當且僅當從第 i 層到第 k 層的總水量都不足以使第 k 層自動減壓時 ci 中會包含第 k 層的強制放水費用. 因為當 k 不變時總水量隨著 i 的減少而單調增加, 所以合法的 i 必然是比 k 小的連續一段, 二分找到最小的 i , 把最小的 i 到 k 之間的所有 ci 都加上 k 的花費即可. 而這個操作可以通過簡單的字首和解決. 總時間複雜度是 O(nlogn).
程式碼展示
#include<bits/stdc++.h> using namespace std; const int maxn = 15e4+10; int n; int w[maxn],l[maxn],p[maxn]; int sum[maxn],v[maxn]; int now,ans = 0x3f3f3f3f; int main(){ scanf("%d",&n); for(int i = 1;i <= n;++i){ scanf("%d%d%d",&w[i],&l[i],&p[i]); sum[i] = sum[i-1] + w[i]; } for(int i = 1;i <= n;++i){ int L = 1,R = i; while(L < R) { int mid = (L + R) >> 1; if(sum[i] - sum[mid - 1] <= l[i])R = mid; if(sum[i] - sum[mid - 1] > l[i])L = mid + 1; } v[R] += p[i]; v[i+1] -= p[i]; } for(int i = 1;i <= n;++i){ v[i] += v[i-1]; if(ans > v[i])ans = v[i],now = i; } printf("%d\n",ans); for(int i = now;i <= n;++i) if(sum[i] - sum[now - 1] <= l[i])printf("%d ",i); return 0; }