---
# System prepended metadata

title: ARC 173B

---

## AtCoder Regular Contest 173B - Make Many Triangles

[<<回主頁](https://hackmd.io/@r1cky/Sk2hKzga6)


### 原題解法

只要任三個點都不在線上，那答案就是 $\lfloor n/3 \rfloor$。

我們發現如果不要有 $2/3$ 個點出現在同一條直線上，那答案還是一樣，因為我們可以用剩下的點來組成 $\lfloor n/3 \rfloor$ 個三角形。

如果超過的話就會影響答案，最直接的還是把同線上的兩兩一組，然後選一個不在線上的。

可以窮舉每兩個點當線，然後枚舉其他點，是 $O(N^3)$，足以通過。

其實似乎也有方法可以用處理斜率的方式降到約 $O(N^2 log N)$ 之類的，但在這題可能不會比較快。

### bonus

同原題，但改變 $N$ 的限制， $N \leq 10^5$。

### bonus 解法

如果用上述的方法應該是無法在時限內通過。

那我們觀察一個性質，我們要找的是最多點的線 $L$，題目要找的要線上有 $> \lfloor \frac{2n}{3}\rfloor$ 點才有用，所以如果這個 $L$ 上根本沒那麼多點，那答案勢必還是 $\lfloor n/3 \rfloor$，那我們可以改變策略，前面窮舉兩個點當線的步驟改用 random 選兩個相異的點。

證明 : 

令 $L$ 上的點有 $a$ 個，要一次選到的機率是 $\frac{a}{n} \cdot \frac{a}{n} = (\frac{a}{n})^2$。

那一次選不到的機率是 $1 - (\frac{a}{n})^2$。

那連續 $b$ 次選不到的機率是 $P = (1 - (\frac{a}{n})^2)^b$。

因為 $a$ 越小 $P$ 會越大，取 $a = \frac{2n}{3}$。 (前面提到的性質 $a$ 不能小於這個數)

所以 $P = (\frac{5}{9})^b$

$b$ 如果只是 $1$ 感覺沒甚麼可信度，但是我們可以把 $b$ 弄大一點，值得注意的是當 $b = 50$，要都沒選到的機率已經低於 $10^{-12}$。 

那這樣的時間複雜度是 $O(bN)$，取一個夠大的 $b$ 就可以。


### code

```cpp=
//Author : rickytsung
//Problem : ARC 173 B
//Date : 2024/3/12
#pragma GCC optimize("O3,unroll-loops")
#pragma GCC target("avx2,bmi,bmi2,lzcnt,popcnt")
#include <bits/stdc++.h>
#define i64 long long
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define pii pair<int, int>
#define f first
#define s second
using namespace std;
mt19937 rng(114514);
int rnd(int l, int r) {
    return uniform_int_distribution<int>(l, r)(rng);
}

int work(int n, pii* xy){
    int pt1 = rnd(0, n-1), cnt = 0;
    int pt2 = pt1;
    while((pt2 = rnd(0, n-1)) == pt1);
    i64 x1 = xy[pt1].f;
    i64 y1 = xy[pt1].s;
    i64 dx = xy[pt2].f - x1;
    i64 dy = xy[pt2].s - y1;
    for(int i = 0; i < n; i++){
        if(dx * (y1 - xy[i].s) == dy * (x1 - xy[i].f)){
            cnt ++;
        }
    }
    return cnt;
}

int main() {
    IOS;
    int n;
    cin >> n;
    int ans = n/3;
    pii xy[n];
    for(int i = 0; i < n; i++){
       cin >> xy[i].f >> xy[i].s;
    }
    for(int i = 0; i < 50; i++){
        ans = min(ans, n - work(n, xy));
    }
    cout << ans;
}
```


