# AP325 Q-3-14. 線性函數(@@)
###### tags: `程式設計`
online judge: TCIRC
link: https://judge.tcirc.tw/ShowProblem?problemid=d038
## 題目敘述
有 $N$ 線性函數 $f_i(x)=a_ix+b_i,1\leq i \leq N$。定義 $F(x)=max_if_i(x)$。輸入 $c[i], 1\leq i \leq m$,請計算 $\sum_{i=1}^nF(c[i])$。
## 輸入說明
第一行是 $N$ 與 $m$。接下來有 $N$ 行,依序每行兩個整數 $a_i$ 與 $b_i$,最後一行有$m$個整數$c[1],c[2],⋯,c[m]$。每一行的相鄰數字間以空白隔開。$N\leq 10^5$, $m\leq 5\times10^4$,輸入整數絕對值不超過 $10^7$,答案不超過 $10^{15}$。
### 範例輸入
```
4 5
-1 0
1 0
-2 -3
2 -3
4 -5 -1 0 2
```
### 範例輸出
```
15
```
**範例的圖**
![](https://i.imgur.com/Mn8Upxh.png =50%x)
### AP325的提示
![](https://i.imgur.com/0nJGJyc.png)
## 想法
先找到提示中,組成紅色線的那些線,並把其他不相關的線刪掉,再記錄紅色線轉彎地方的點的$x$座標,以知道哪些$c[i]$要用哪一條線去算$y$值,最後再將$c[i]$由小到大,依序求解。
### 1.尋找符合的線
維護一個stack,裡面的線皆是當前的解,依斜率由小到大加入,如果即將加入的線,比他前面的線還好時,就可以把前面的線pop掉。
- 如何判斷當前的線是否比上一條好?
![](https://i.imgur.com/zd71SCG.png =50%x)![](https://i.imgur.com/9HQkXpA.png =50%x)
數字代表放入stack的順序,令$x_{a,b}$代表**直線a**和**直線b**交的$x$座標。
當**直線3**沒有比**直線2**好的時候,如左圖,$x_{1,2} \lt x_{2,3}$。
而**直線3**比**直線2**好的時候,如右圖,$x_{1,2} \gt x_{2,3}$。
所以得到一個結論,當$x_{n-1,n-2} \gt x_{n,n-1}$,**直線n-1**就不會是可以找到最大值的線。
- 例外
當兩直線平行時,會找不到兩線所交的點,此時就按照兩線的$b$來比,較大的會在較上面,所以就捨棄掉較小的。
### 2.找到各c[i]的最大y值
過濾掉一些不會用到的線之後,用一個陣列去紀錄線與線之間的$x$座標,去找$c[i]$所對應到的直線,並帶入求解。(參考Code 67 ~ 73行)
## Code
```cpp=
#include<iostream>
#include<algorithm>
#define a first
#define b second
#define x first
#define y second
#define MAX_N 100020
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
pll line[MAX_N];
pll dq[MAX_N]; // actually it's a stack
ll C[MAX_N];
double pos[MAX_N];
int lf = 0, rt = 0;
pdd getPoint(pll p1, pll p2) {
pdd res;
double a1 = p1.a, b1 = p1.b;
double a2 = p2.a, b2 = p2.b;
res.x = (b2 - b1) / (a1 - a2);
res.y = (a2*b1 - a1*b2) / (a2 - a1);
return res;
}
bool discardLast(int idx) {
if (line[idx].a == dq[rt-1].a) {
return true;
}
pdd p = getPoint(line[idx], dq[rt-1]);
pdd lp = getPoint(dq[rt-1], dq[rt-2]);
if (p.x > lp.x) {
return false;
} else {
return true;
}
}
int main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
int N, M;
cin >> N >> M;
for (int i = 0;i < N;i++) {
cin >> line[i].a >> line[i].b;
}
for (int i = 0;i < M;i++) {
cin >> C[i];
}
sort(line, line+N);
sort(C, C+M);
for (int i = 0;i < N;i++) {
while (rt - lf >= 2 && discardLast(i)) {
--rt;
}
dq[rt++] = line[i];
}
for (int i = lf, idx = 0;i <= rt-2;i++, idx++) {
pdd p = getPoint(dq[i], dq[i+1]);
pos[idx] = p.x;
}
ll ans = 0;
int idx = 0;
for (int i = 0;i < M;i++) {
while (idx < rt-lf-1 && C[i] > pos[idx]) {
idx++;
}
pll p = dq[lf+idx];
ans += (p.a*C[i] + p.b);
}
cout << ans << '\n';
return 0;
}
```
### Result
![](https://i.imgur.com/hFUzHAK.png)
- 最後更新 [time=Fri, Sep 24, 2021 10:54 AM]