推性质 + 绝对值函数处理:ABC479G

题目给定的式子是:

(Ai+ci)(Bj+dj)=(Aj+cj)(Bi+di)(A_i+c_i)(B_j+d_j)=(A_j+c_j)(B_i+d_i)

先除过去:

Ai+ciBi+di=Aj+cjBj+dj\frac {A_i+c_i}{B_i + d_i}=\frac {A_j+c_j}{B_j+d_j}

设上式为一个定值 pq\dfrac p q

Ai+ciBi+di=pq\frac {A_i+c_i}{B_i + d_i}=\frac p q

移项得到:

ci=1q(pBi−qAi+di)c_i=\frac 1 q(pB_i-qA_i+d_i)

因此:

∣di∣+∣ci∣=∣di∣+∣1q(pBi−qAi+di)∣|d_i|+|c_i|=|d_i|+|\frac 1 q(pB_i-qA_i+d_i)|

这是两个绝对值函数相加。

image-20261012172957386

一个斜率是 11,一个是 pq\dfrac p q

因此,零点在哪,之和 p,qp,q 关系有关。

但是,实际上,它的最值一定在两个零点之一处取到。而且在哪个之和 p,qp,q 有关,和 ii 无关。

而一个零点是 ∣di∣=0|d_i|=0,而另一个是 ∣ci∣=0|c_i|=0.

因此,要么 did_i 全是0,要么 cic_i 全是0。

两种情况翻转一下就能做了。

假设 ci=0c_i=0:

AiBi+di=pq\frac {A_i}{B_i+d_i}=\frac p q

我们设 x=qpx=\dfrac q p,而我们求的 :∑∣di∣=∑∣Aix−Bi∣\sum |d_i|=\sum |A_ix-B_i|

然后现在变成一堆绝对值函数相加,求其最小值。

这个和上面是一样的,我们可以唯一确定零点在哪里。先对所有零点(BiAi\dfrac {B_i}{A_i})排序,然后从左往右扫,维护从这个零点到下一个零点的斜率。当斜率从负转正的时候,这个零点就是我们需要的零点了。

然后带回去求 ∑∣Aix−Bi∣\sum |A_ix-B_i| 就行了。需要注意的是,如果 AiA_i 为0,说明 xx 取多少都无影响,那样子就恒贡献 BiB_i 了。

时间复杂度 O(nlog⁡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 M
//#define mo
#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]);
// for(auto t : G) t.A.work();
// for(auto t : G) debug("%lld/%lld ", t.x, t.y); debug("\n");
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);
// for(auto t : G) debug("%lld/%lld ", t.x, t.y); debug("\n");
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
// srand(time(NULL));
// T = read();
// while(T--) {
//
// }
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;
}