https://codeforces.com/contest/1194/problem/E
这道题是ZOJ1426的超级强化版,数据大了很多。给了两种解法,前一种思维简单,好理解。
题意:给了n条线段,线段不重复,而且不是竖线就是横线,问这n条线段能围城多少个矩形。
做法:首先,由于n有5000,对半开枚举横线和竖线是不行的。
我们这样考虑,我们先把竖线和横线分别存储起来。然后可以往暴力一点方向想,最后在进行优化。
稍微暴力一点的就是先判断一下,每一条竖线与多少条竖线相交,然后在判断任意两条竖线,公共的相交横线有几条,然后取一个组合数,就是对答案的一部分贡献。
但是怎么记录呢?事实证明数组标记然后暴力判断是会T的。
但不过有一个bitset的东西,我们开几个bitset,如果第i条竖线与第j条横线相交,那么第i个bitset的第j位置为1;
然后判断第i和第j条竖线公共的相交横线有几条,只需要将i和j相&然后count就行了,事实证明这样的复杂度可以接受的。
就下来的复杂的就与竖线的条数有关,最大有5000这个多半会T,事实也T了,既然竖线多了,那么横线就少了,所以可以将竖线和横线对换,这样这道题就可以过了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = 200000 + 10;
const int inf = 0x3f3f3f3f;
struct node {
int x1, y1, x2, y2;
} line[N];
vector <node> v1, v2;
int n;
int belong(node a, node b) {
if (a.x1 >= b.x1 && a.x1 <= b.x2 && b.y1 >= a.y1 && b.y1 <= a.y2)
return 1;
if (b.x1 >= a.x1 && b.x1 <= a.x2 && a.y1 >= b.y1 && a.y1 <= b.y2)
return 1;
return 0;
}
bitset<5010> mp[5010];
int main() {
scanf("%d", &n);
int a, b, c, d;
for (int i = 1; i <= n; i++) {
scanf("%d%d%d%d", &a, &b, &c, &d);
if (a == c) {
if (b > d)swap(b, d);
v1.push_back(node{a, b, c, d});
}
if (b == d) {
if (a > c)swap(a, c);
v2.push_back(node{a, b, c, d});
}
}
ll ans = 0;
if (v1.size() > v2.size()) swap(v1, v2);
for (int i = 0; i < v1.size(); i++) {
for (int j = 0; j < v2.size(); j++) {
if (belong(v1[i], v2[j]))
mp[i][j] = 1;
}
for (int j = 0; j < i; j++) {
ll ret = (mp[i] & mp[j]).count();
ans += ret * (ret - 1) / 2;
}
}
cout << ans << endl;
return 0;
}
但不过上面这个复杂度也不怎么行,还容易被卡。因此可以用一种数据结构对公共相交线进行优化。
首先对竖线按照x进行排序,然后对第i条竖线相交的横线全部记录下来。
然后对其按照左端点进行排序,也可以右端点。
对于第j条竖线,进行遍历由于有序,把左端点小于j的x的横线记录下来。
这些横线在j条竖线的y1,y2的范围相交。然后用树状数组求和就完了。
如果不清楚画一个图就清楚了,具体这道题如何也只有本人自己做了才清楚。
#include "bits/stdc++.h"
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = 2e5 + 10;
const int M = 5002;
struct line {
int p, l, r;
bool operator<(line a) const { return p < a.p; }
} p[N], q[N];
struct node {
int p, r;
bool operator<(node a) const { return r < a.r; }
} l[N];
int n, cnt1, cnt2, low[M * 2];
void update(int x, int val) { for (; x < M * 2; x += x & -x) low[x] += val; }
int query(int x) {
int ret = 0;
for (; x; x -= x & -x) ret += low[x];
return ret;
}
int belong(line v, line h) {
if (v.p >= h.l && v.p <= h.r && h.p >= v.l && h.p <= v.r)
return 1;
return 0;
}
int main() {
scanf("%d", &n);
int a, b, c, d;
for (int i = 1; i <= n; i++) {
scanf("%d%d%d%d", &a, &b, &c, &d);
a += M, b += M, c += M, d += M;
if (a == c) {
if (b > d) swap(b, d);
p[++cnt1] = line{a, b, d};
}
if (b == d) {
if (a > c) swap(a, c);
q[++cnt2] = line{b, a, c};
}
}
ll ans = 0;
sort(p + 1, p + cnt1 + 1);
for (int i = 1; i <= cnt1; i++) {
int cnt = 0;
for (int j = 1; j <= cnt2; j++) {
if (belong(p[i], q[j])) {
l[++cnt] = node{q[j].p, q[j].l};
}
}
sort(l + 1, l + cnt + 1);
memset(low, 0, sizeof(low));
for (int j = 1, k = 1; j < i; j++) {
for (; k <= cnt && l[k].r <= p[j].p; k++) update(l[k].p, 1);
ll ret = query(p[j].r) - query(p[j].l - 1);
ans += ret * (ret - 1) / 2;
}
}
printf("%lld\n", ans);
return 0;
}