P6149 学习笔记
省流:又是贪心。。
经典。。
题目大意
平面上给出了 \(n\) 个点,第 \(i\) 个点的坐标为 \((x_i,y_i)\),现在要求从其中选择三个点组成一个三角形,使得三角形有一条边与 \(x\) 轴平行,还有一条边与 \(y\) 轴平行,求所有满足条件的三角形面积的两倍之和,并对 \(10^9+7\) 取模。
分析
三角形有一条边与 \(x\) 轴平行,还有一条边与 \(y\) 轴平行,这说明这一定是一个直角三角形。
我们设直角顶点为 \(P(x_0,y_0)\),则另外两个点分别位于水平线 \(y=y_0\) 和垂直线 \(x=x_0\) 上。
则这个三角形的面积为 \(\frac12 (x-x_0)(y-y_0)\),两倍就是 \((x-x_0)(y-y_0)\),规避了分数。其中 \(x\) 是水平线上另一个点的横坐标,\(y\) 是垂直线上另一个点的纵坐标)
因此,对于每个点 \(P\):
-
若水平方向(相同 \(y\))上有 \(m\) 个其他点,横坐标分别为 \(x_1,\dots,x_m\);
-
垂直方向(相同 \(x\))上有 \(n\) 个其他点,纵坐标分别为 \(y_1,\dots,y_n\)。
于是最终的答案为
所以我们只需要快速求出每个点的水平方向距离之和与垂直方向的距离之和,累加即可。
下面考虑如何求出水平方向距离之和与垂直方向的距离之和。
对于一组相同 \(y\) 的点(即水平方向),将其按 \(x\) 排序后得到序列 \(a_1,a_2,\dots,a_k\)。
对于第 \(i\) 个点,它与组内其他所有点的 \(x\) 距离之和为:
-
左边 \(i-1\) 个点:\(a_i(i-1)-\sum_{j=1}^{i-1}a_j\);
-
右边 \(k-i\) 个点:\((\sum_{j=i+1}^{k} a_j)-a_i(k-i)\)。
可以计算前缀和 \(pre_i\),则:
-
左边贡献 = \(a_i(i-1)-pre_{i-1}\);
-
右边贡献 = \((pre_m-pre_i)-a_i(k-i)\)。
两者相加即为该点的水平距离和。垂直方向同理。
code
ll n;
ll x[maxn], y[maxn];
ll ans, hdis[maxn], vdis[maxn];
ll a[maxn], idx[maxn];
ll all_sum, presum[maxn];
unordered_map <ll, vector <pii> > byx;
unordered_map <ll, vector <pii> > byy;
int main() {
freopen("std.in", "r", stdin);
freopen("std.out", "w", stdout);
fast;
cin >> n;
for (ll i = 1; i <= n; i++)
cin >> x[i] >> y[i];
for (ll i = 1; i <= n; i++)
byx[x[i]].emplace_back(y[i], i);
for (ll i = 1; i <= n; i++)
byy[y[i]].emplace_back(x[i], i);
for (auto &[p, vec] : byx){
sort (vec.begin(), vec.end());
ll m = vec.size();
all_sum = 0;
// memset(a, 0, sizeof a);
// memset(idx, 0, sizeof idx);
// memset(presum, 0, sizeof presum); 其实不需要清零,不然会 T 飞
for (int i = 1; i <= m; i++){
a[i] = vec[i - 1].first;
idx[i] = vec[i - 1].second;
}
for (int i = 1; i <= m; i++)
presum[i] = presum[i - 1] + a[i];
all_sum = presum[m];
for (int i = 1; i <= m; i++)
vdis[idx[i]] = a[i] * (i - 1) - presum[i - 1] + (all_sum - presum[i]) - a[i] * (m - i);
}
for (auto &[p, vec] : byy){
sort (vec.begin(), vec.end());
ll m = vec.size();
all_sum = 0;
// memset(a, 0, sizeof a);
// memset(idx, 0, sizeof idx);
// memset(presum, 0, sizeof presum); 其实不需要清零,不然会 T 飞
for (int i = 1; i <= m; i++){
a[i] = vec[i - 1].first;
idx[i] = vec[i - 1].second;
}
for (int i = 1; i <= m; i++)
presum[i] = presum[i - 1] + a[i];
all_sum = presum[m];
for (int i = 1; i <= m; i++)
hdis[idx[i]] = a[i] * (i - 1) - presum[i - 1] + (all_sum - presum[i]) - a[i] * (m - i);
}
for (ll i = 1; i <= n; i++)
ans = (ans + (vdis[i] % mod) * (hdis[i] % mod) % mod) % mod;
cout << ans;
return 0;
}

浙公网安备 33010602011771号