\(\text{QOJ1838 Intellectual Implementation 题解}\)
其实这个题还是比较良心的,原因是保证了 \(x,y\) 互不相同,避免了一些 sb 的边界处理。
先转化一下,把每个矩形看作一个点,有交则连边,那么实际上就是求满足两两无边的三元组 \((a,b,c)\) 的个数。直接做显然没法处理,考虑容斥,设三元组有一/二/三条边的个数分别为 \(c_1,c_2,c_3\),那么 \(ans={n\choose 3}-c_1-c_2-c_3\)。
全部求出来还是有点吃操作了,考虑建立一些等式代换一下。对于每两个点来考虑,会有 \((n-2)\sum d_i=2c_1+4c_2+6c_3\);从每个点来考虑,有 \(\sum{d_i\choose 2}=c_2+3c_3\),那么简单变换一发现只需要求出 \(d_i\) 和 \(c_3\) 即可。
矩形问题我们按照 \(x\) 轴扫描线处理。对于 \(d_i\),我们开一些树状数组维护一下 \(y\) 轴的位置,那么与它有交的矩阵个数就是上边下面的下边个数减去下边下面的上边个数,这个东西画一画就能得到,但注意减去多算的自己。
对于 \(c_3\),有套路是将贡献挂在 \(x_1\) 最大的矩形上。那么对于另外矩形,若都与这个矩形的 \(x\) 轴相交由扫描线的过程可以得到这两个矩形自己 \(x\) 轴也是相交的,那么现在要求的是 \(y\) 轴同样有交的情况。
还是考虑容斥,先统计所有 \(x_1\) 小于当前矩形且有交的矩形数量,然后要减去这两个矩形之间没有交集的情形。那么实际上就是其中一个的 \(y_2\) 小于另一个的 \(y_1\),然后你会发现这个东西我们在扫描线的时候动态加删边,维护一个线段树合并信息就能做了,然后这个题就做完了。
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 4e5 + 5;
int C_2(int n) {return n * (n - 1) / 2;
}
int C_3(int n) {return n * (n - 1) * (n - 2) / 6;
}
int n;
struct node {int x1, x2, y1, y2;
} e[N];
int b[N], tot;struct BIT {int lbt(int x) {return x & (-x);}int tr[N];void add(int x, int v) {while (x <= 2 * n) {tr[x] += v;x += lbt(x);}}int ask(int x) {if (x <= 0) return 0;int ans = 0;while (x) {ans += tr[x];x -= lbt(x);}return ans;}
} l1, l2, r1, r2, t1, t2;struct Node {int lct, rct, ans;
} t[N << 2];
#define lct(i) t[i].lct
#define rct(i) t[i].rct
#define ans(i) t[i].ans
#define lc (p << 1)
#define rc (lc | 1)
Node operator + (Node a, Node b) {return {a.lct + b.lct, a.rct + b.rct, a.ans + b.ans + a.lct * b.rct};
}
void updl(int p, int x, int v, int pl = 1, int pr = 2 * n) {if (pl == pr) return lct(p) += v, void();int mid = (pl + pr) >> 1;if (x <= mid) updl(lc, x, v, pl, mid);else updl(rc, x, v, mid + 1, pr);t[p] = t[lc] + t[rc];
}
void updr(int p, int x, int v, int pl = 1, int pr = 2 * n) {if (pl == pr) return rct(p) += v, void();int mid = (pl + pr) >> 1;if (x <= mid) updr(lc, x, v, pl, mid);else updr(rc, x, v, mid + 1, pr);t[p] = t[lc] + t[rc];
}
Node query(int p, int l, int r, int pl = 1, int pr = 2 * n) {if (l <= pl && pr <= r) return t[p];int mid = (pl + pr) >> 1;if (r <= mid) return query(lc, l, r, pl, mid);if (l > mid) return query(rc, l, r, mid + 1, pr);return query(lc, l, r, pl, mid) + query(rc, l, r, mid + 1, pr);
}
int id[N], deg[N];signed main() {ios::sync_with_stdio(0);cin.tie(0);cin >> n;for (int i = 1; i <= n; i++) cin >> e[i].x1 >> e[i].x2 >> e[i].y1 >> e[i].y2;for (int i = 1; i <= n; i++) b[++tot] = e[i].x1, b[++tot] = e[i].x2;sort(b + 1, b + 1 + tot);for (int i = 1; i <= n; i++) {e[i].x1 = lower_bound(b + 1, b + 1 + tot, e[i].x1) - b;e[i].x2 = lower_bound(b + 1, b + 1 + tot, e[i].x2) - b;}tot = 0;for (int i = 1; i <= n; i++) b[++tot] = e[i].y1, b[++tot] = e[i].y2;sort(b + 1, b + 1 + tot);for (int i = 1; i <= n; i++) {e[i].y1 = lower_bound(b + 1, b + 1 + tot, e[i].y1) - b;e[i].y2 = lower_bound(b + 1, b + 1 + tot, e[i].y2) - b;}for (int i = 1; i <= n; i++) {id[e[i].x1] = i;id[e[i].x2] = -i;}int c3 = 0, sm = 0;for (int i = 1; i <= 2 * n; i++) {int x = id[i];if (x > 0) {deg[x] -= r1.ask(e[x].y2) - r2.ask(e[x].y1 - 1);c3 += C_2(t1.ask(e[x].y2) - t2.ask(e[x].y1 - 1)) - query(1, e[x].y1, e[x].y2).ans;l1.add(e[x].y1, 1);l2.add(e[x].y2, 1);t1.add(e[x].y1, 1);t2.add(e[x].y2, 1);updl(1, e[x].y2, 1);updr(1, e[x].y1, 1);}else {x *= -1;deg[x] += l1.ask(e[x].y2) - l2.ask(e[x].y1 - 1);r1.add(e[x].y1, 1);r2.add(e[x].y2, 1);t1.add(e[x].y1, -1);t2.add(e[x].y2, -1);updl(1, e[x].y2, -1);updr(1, e[x].y1, -1); }}for (int i = 1; i <= n; i++) {--deg[i];sm += (n - 2) * deg[i];sm -= 2 * C_2(deg[i]);}sm /= 2;cout << C_3(n) - sm - c3 << '\n';return 0;
}