## 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; } ```