推性质 + 绝对值函数处理:ABC479G
题目给定的式子是:
( A i + c i ) ( B j + d j ) = ( A j + c j ) ( B i + d i ) (A_i+c_i)(B_j+d_j)=(A_j+c_j)(B_i+d_i)
( A i + c i ) ( B j + d j ) = ( A j + c j ) ( B i + d i )
先除过去:
A i + c i B i + d i = A j + c j B j + d j \frac {A_i+c_i}{B_i + d_i}=\frac {A_j+c_j}{B_j+d_j}
B i + d i A i + c i = B j + d j A j + c j
设上式为一个定值 p q \dfrac p q q p
A i + c i B i + d i = p q \frac {A_i+c_i}{B_i + d_i}=\frac p q
B i + d i A i + c i = q p
移项得到:
c i = 1 q ( p B i − q A i + d i ) c_i=\frac 1 q(pB_i-qA_i+d_i)
c i = q 1 ( p B i − q A i + d i )
因此:
∣ d i ∣ + ∣ c i ∣ = ∣ d i ∣ + ∣ 1 q ( p B i − q A i + d i ) ∣ |d_i|+|c_i|=|d_i|+|\frac 1 q(pB_i-qA_i+d_i)|
∣ d i ∣ + ∣ c i ∣ = ∣ d i ∣ + ∣ q 1 ( p B i − q A i + d i ) ∣
这是两个绝对值函数相加。
一个斜率是 1 1 1 ,一个是 p q \dfrac p q q p
因此,零点在哪,之和 p , q p,q p , q 关系有关。
但是,实际上,它的最值一定在两个零点之一处取到。而且在哪个之和 p , q p,q p , q 有关,和 i i i 无关。
而一个零点是 ∣ d i ∣ = 0 |d_i|=0 ∣ d i ∣ = 0 ,而另一个是 ∣ c i ∣ = 0 |c_i|=0 ∣ c i ∣ = 0 .
因此,要么 d i d_i d i 全是0,要么 c i c_i c i 全是0。
两种情况翻转一下就能做了。
假设 c i = 0 c_i=0 c i = 0 :
A i B i + d i = p q \frac {A_i}{B_i+d_i}=\frac p q
B i + d i A i = q p
我们设 x = q p x=\dfrac q p x = p q ,而我们求的 :∑ ∣ d i ∣ = ∑ ∣ A i x − B i ∣ \sum |d_i|=\sum |A_ix-B_i| ∑ ∣ d i ∣ = ∑ ∣ A i x − B i ∣
然后现在变成一堆绝对值函数相加,求其最小值。
这个和上面是一样的,我们可以唯一确定零点在哪里。先对所有零点(B i A i \dfrac {B_i}{A_i} A i B i )排序,然后从左往右扫,维护从这个零点到下一个零点的斜率。当斜率从负转正的时候,这个零点就是我们需要的零点了。
然后带回去求 ∑ ∣ A i x − B i ∣ \sum |A_ix-B_i| ∑ ∣ A i x − B i ∣ 就行了。需要注意的是,如果 A i A_i A i 为0,说明 x x x 取多少都无影响,那样子就恒贡献 B i B_i B i 了。
时间复杂度 O ( n log n ) O(n\log n) O ( n log n ) ,瓶颈在排序复杂度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 #include <bits/stdc++.h> #include <cassert> using namespace std;bool Mbe;#ifdef LOCAL #define debug(...) fprintf(stdout, ##__VA_ARGS__) #else #define debug(...) void(0) #endif #define int long long inline int read () {int x=0 ,f=1 ;char ch=getchar ();while (ch<'0' ||ch>'9' ){if (ch=='-' )f=-1 ;ch=getchar ();}while (ch>='0' &&ch<='9' ){x=(x<<1 )+ (x<<3 )+(ch^48 );ch=getchar ();}return x*f;} #define Z(x) (x)*(x) #define pb push_back #define fi first #define se second #define N 200010 struct node { int x, y; void work () { if (!x) return y = 1 , void (); assert (y); int z = __gcd(abs (x), abs (y)); x /= z; y /= z; if (x < 0 && y < 0 ) x = -x, y = -y; } }C, ans; struct Node { node A; int i; }; int n, m, i, j, k, T; int A[N], B[N]; vector<Node>G; bool operator < (node A, node B) { if (A.y * B.y > 0 ) return (__int128)A.x * B.y < (__int128)B.x * A.y; else return (__int128)A.x * B.y > (__int128)B.x * A.y; } node min (node A, node B) { return A < B ? A : B; } node calc () { int sumA = 0 ; G.clear (); for (i = 1 ; i <= n; ++i) if (A[i]) G.pb ({(node){B[i], A[i]}, i}), sumA += abs (A[i]); sort (G.begin (), G.end (), [] (Node A, Node B) { return A.A < B.A; }); m = G.size (); if (!m) C.x = 0 , C.y = 1 ; else { auto TT = *G.begin (); C = TT.A; for (auto t : G) { sumA -= abs (A[t.i]) * 2 ; C = t.A; if (sumA < 0 ) break ; } } for (auto t : G) debug ("%lld/%lld[%lld] " , t.A.x, t.A.y, (int )(t.A < C)); debug ("\n" ); debug ("(%lld %lld)\n" , C.x, C.y); int sum = 0 ; for (i = 1 ; i <= n; ++i) { if (!A[i]) sum += abs (B[i] * C.y); else sum += abs (A[i] * C.x - B[i] * C.y); debug ("[%lld %lld]Sum become : %lld\n" , A[i], B[i], sum); } node t = {sum, abs (C.y)}; t.work (); debug ("t : %lld %lld\n" , t.x, t.y); return t; } bool Med; signed main () { #ifdef LOCAL freopen ("in.txt" , "r" , stdin); freopen ("out.txt" , "w" , stdout); atexit ([]{fprintf (stderr,"%.lfms\n%lfMB\n" ,clock ()*1000. /CLOCKS_PER_SEC,(&Mbe-&Med)/1024. /1024 );}); #endif n = read (); for (i = 1 ; i <= n; ++i) A[i] = read (); for (i = 1 ; i <= n; ++i) B[i] = read (); ans = calc (); for (i = 1 ; i <= n; ++i) swap (A[i], B[i]); ans = min (ans, calc ()); printf ("%lld %lld\n" , ans.x, ans.y); return 0 ; }