题解
对于 \(20\%\) 的数据:
直接暴力 \(O(n^3)\) 枚举即可。
对于 \(35\%\) 的数据:
不妨将有交的矩形连边,不难发现最终需要统计反图中三元环个数,考虑原图中任意三个点的关系:
设上述情况在原图中的出现次数分别为 \(x_1, x_2, x_3, x_4\) ,那么我们需要求解 \(x_1\) ,容易发现 \(x_1+x_2+x_3+x_4=\binom{n}{3}\) ,考虑统计每个点的度数 \(deg_i\) ,并求解 \(\sum deg_i(n-deg_i-1)\) ,容易发现这实际上等于 \(2x_2+2x_3\) ,由于特殊性质保证 \(x_4=0\) ,因此直接暴力建图进行统计即可。
复杂度 \(O(n^2)\) 。
对于 \(50\%\) 的数据:
考虑快速求解每个点的度数,由于 \(deg_i\) 为与矩形 \(i\) 有交的矩形个数,因此考虑扫描线解决原问题。
我们分两种情况讨论:
-
\(l_j<l_i\) ,我们在 \(l_j\) 的位置插入线段 \([u_j, v_j]\) ,在 \(r_j\) 的位置删除线段 \([u_j, v_j]\) ,容易发现只需要在 \(l_i\) 位置上统计与 \([u_i, v_i]\) 相交的线段个数,可以通过计算 \(v_j<u_i\) 和 \(u_j>v_i\) 的线段个数求解。
-
\(l_j>l_i\) ,我们在 \(l_j\) 的位置插入线段 \([u_j, v_j]\) ,不难发现我们查询的是满足 \(l_i<l_j<r_i\) 并且 \([u_j, v_j]\) 与 \([u_i, v_i]\) 有交的线段个数,将询问简单差分后很容易查询。
使用树状数组维护,复杂度 \(O(n\log n)\) 。
对于 \(100\%\) 的数据:
考虑快速统计原图中三元环的个数。
假设对于三元组 \((i, j, k)\) ,满足矩形 \(i, j, k\) 两两有交,考虑在 \(\max(l_i, l_j, l_k)\) 的位置统计答案,具体来讲,仍然使用扫描线解决问题,一个矩形 \(x\) ,我们在 \(l_x\) 插入线段 \([u_x, v_x]\) ,在 \(r_x\) 删除线段 \([u_x, v_x]\) ,如果我们的线段树可以快速维护集合中相交的线段对数,那么我们可以在插入 \((i, j, k)\) 三元组中最后一条线段前查询集合中相交线段对数,将最后一条线段插入,再次查询集合中相交线段对数,这样我们可以求解与当前插入线段有交的相交线段对数,也就是包含当前矩形的一部分三元环,由于一组三元环一定会在最后一个矩形被插入的位置被统计到,因此可以保证正确性。
考虑维护这棵线段树,对于一组相交的线段,比较自然的想法是在相交的位置统计其贡献,因此考虑维护 \(val_i=\sum_{x\in S}[l_x\le i\le r_x], val'_i=\sum_{x\in S}[l_x\le i<r_x]\) ,那么相交线段对数为 \(\sum\binom{val_i}{3}-\sum\binom{val'_i}{3}\) ,设当前这对相交的线段相交部分长度为 \(l\) ,容易发现这对线段会对 \(l\) 个位置的 \(val_i\) 产生贡献,对 \(l-1\) 个位置的 \(val'_i\) 产生贡献,因此直接相减可以将当前相交线段的贡献恰好统计一次。
那么线段树只需要支持区间修改即可。
考虑如何下放懒惰标记,容易发现这等价于快速求解 \(\binom{x+y}{t}\) ,简单展开有:
\[\binom{x+y}{t}=\sum_{k=0}^{t}\binom{x}{k}\binom{y}{t-k} \]由于 \(y\) 可能为负数,因此需要使用广义二项式。
直接在线段树每个节点维护 \(\sum \binom{val_i}{t}, t\le 3\) 即可快速下方懒惰标记。
总复杂度 \(O(n\log n)\) ,当然常数飞天。
按照理论上界来讲,线段树每个节点维护信息达到 \(O(n^4)\) 级别,因此需要使用 __int128 ,但由于出题人十分善良,因此并没有卡这个上界。(才不是因为不会卡呢!)
code
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int max1 = 2e5;
int n;
struct Matrix
{
int X1, X2, Y1, Y2, id;
}matrix[max1 + 5];
int save[max1 * 2 + 5], lenx, leny;
int degree[max1 + 5];
long long ans;
long long cnt;
struct Bit
{
int tree[max1 * 2 + 5], lim;
#define lowbit(now) ( now & -now )
void Clear ( int __lim )
{ lim = __lim; memset(tree + 1, 0, sizeof(int) * __lim); return; }
void Insert ( int now, int x )
{
while ( now <= lim )
{
tree[now] += x;
now += lowbit(now);
}
return;
}
int Query ( int now )
{
int res = 0;
while ( now )
{
res += tree[now];
now -= lowbit(now);
}
return res;
}
int Query ( int L, int R )
{ return Query(R) - Query(L - 1); }
}Tree1, Tree2;
int sum;
long long C ( int n, int m )
{
long long res = 1;
for ( int i = 1; i <= m; i ++ )
res = res * ( n - i + 1 );
for ( int i = 1; i <= m; i ++ )
res = res / i;
return res;
}
struct Segment_Tree
{
#define lson(now) ( now << 1 )
#define rson(now) ( now << 1 | 1 )
struct Data
{
long long sum[2][4];
int tag[2];
}tree[max1 * 8 + 5];
void Build ( int now, int L, int R )
{
memset(tree[now].sum, 0, sizeof(tree[now].sum));
tree[now].sum[0][0] = tree[now].sum[1][0] = R - L + 1;
tree[now].tag[0] = tree[now].tag[1] = 0;
if ( L == R )
return;
int mid = L + R >> 1;
Build(lson(now), L, mid);
Build(rson(now), mid + 1, R);
return;
}
void Update ( int now, int x, int num )
{
Data tmp = tree[now];
long long d[4] = { C(x, 0), C(x, 1), C(x, 2), C(x, 3) };
for ( int i = 0; i < 4; i ++ )
{
tree[now].sum[num][i] = 0;
tree[now].sum[num ^ 1][i] = tmp.sum[num ^ 1][i];
for ( int k = 0; k <= i; k ++ )
tree[now].sum[num][i] += tmp.sum[num][k] * d[i - k];
}
tree[now].tag[num] = tmp.tag[num] + x;
tree[now].tag[num ^ 1] = tmp.tag[num ^ 1];
return;
}
void Push_Up ( int now )
{
for ( int i = 0; i < 4; i ++ )
{
tree[now].sum[0][i] = tree[lson(now)].sum[0][i] + tree[rson(now)].sum[0][i];
tree[now].sum[1][i] = tree[lson(now)].sum[1][i] + tree[rson(now)].sum[1][i];
}
return;
}
void Push_Down ( int now )
{
if ( tree[now].tag[0] )
Update(lson(now), tree[now].tag[0], 0), Update(rson(now), tree[now].tag[0], 0);
if ( tree[now].tag[1] )
Update(lson(now), tree[now].tag[1], 1), Update(rson(now), tree[now].tag[1], 1);
tree[now].tag[0] = tree[now].tag[1] = 0;
return;
}
void Insert ( int now, int L, int R, int ql, int qr, int x, int num )
{
if ( L >= ql && R <= qr )
return Update(now, x, num);
Push_Down(now);
int mid = L + R >> 1;
if ( ql <= mid )
Insert(lson(now), L, mid, ql, qr, x, num);
if ( qr > mid )
Insert(rson(now), mid + 1, R, ql, qr, x, num);
Push_Up(now);
return;
}
long long Query ( int num )
{
return tree[1].sum[num][3];
}
}Tree3;
struct Question
{
int L, R, id;
Question () {}
Question ( int __L, int __R, int __id )
{ L = __L, R = __R, id = __id; }
};
int add1[max1 * 2 + 5], add2[max1 * 2 + 5];
Question qus[max1 * 2 + 5];
int main ()
{
scanf("%d", &n);
for ( int i = 1; i <= n; i ++ )
scanf("%d%d%d%d", &matrix[i].X1, &matrix[i].X2, &matrix[i].Y1, &matrix[i].Y2), matrix[i].id = i;
for ( int i = 1; i <= n; i ++ )
save[i] = matrix[i].X1, save[n + i] = matrix[i].X2;
sort(save + 1, save + 1 + n + n); lenx = unique(save + 1, save + 1 + n + n) - (save + 1);
for ( int i = 1; i <= n; i ++ )
matrix[i].X1 = lower_bound(save + 1, save + 1 + lenx, matrix[i].X1) - save,
matrix[i].X2 = lower_bound(save + 1, save + 1 + lenx, matrix[i].X2) - save;
for ( int i = 1; i <= n; i ++ )
save[i] = matrix[i].Y1, save[n + i] = matrix[i].Y2;
sort(save + 1, save + 1 + n + n); leny = unique(save + 1, save + 1 + n + n) - (save + 1);
for ( int i = 1; i <= n; i ++ )
matrix[i].Y1 = lower_bound(save + 1, save + 1 + leny, matrix[i].Y1) - save,
matrix[i].Y2 = lower_bound(save + 1, save + 1 + leny, matrix[i].Y2) - save;
for ( int i = 1; i <= lenx; i ++ )
add1[i] = add2[i] = 0, qus[i] = Question(0, 0, 0);
for ( int i = 1; i <= n; i ++ )
{
add1[matrix[i].X1] = matrix[i].Y1;
add1[matrix[i].X2] = -matrix[i].Y1;
add2[matrix[i].X1] = matrix[i].Y2;
add2[matrix[i].X2] = -matrix[i].Y2;
qus[matrix[i].X1] = Question(matrix[i].Y1, matrix[i].Y2, matrix[i].id);
}
Tree1.Clear(leny), Tree2.Clear(leny), sum = 0;
for ( int i = 1; i <= lenx; i ++ )
{
if ( qus[i].id )
degree[qus[i].id] += sum - Tree1.Query(qus[i].R + 1, leny) - Tree2.Query(1, qus[i].L - 1);
if ( add1[i] )
{
if ( add1[i] > 0 )
Tree1.Insert(add1[i], 1), ++sum;
else
Tree1.Insert(-add1[i], -1), --sum;
}
if ( add2[i] )
{
if ( add2[i] > 0 )
Tree2.Insert(add2[i], 1);
else
Tree2.Insert(-add2[i], -1);
}
}
for ( int i = 1; i <= lenx; i ++ )
add1[i] = add2[i] = 0, qus[i] = Question(0, 0, 0);
for ( int i = 1; i <= n; i ++ )
{
add1[matrix[i].X1] = matrix[i].Y1;
add2[matrix[i].X1] = matrix[i].Y2;
qus[matrix[i].X1] = Question(matrix[i].Y1, matrix[i].Y2, -matrix[i].id);
qus[matrix[i].X2] = Question(matrix[i].Y1, matrix[i].Y2, matrix[i].id);
}
Tree1.Clear(leny), Tree2.Clear(leny), sum = 0;
for ( int i = 1; i <= lenx; i ++ )
{
if ( qus[i].id )
{
if ( qus[i].id > 0 )
degree[qus[i].id] += sum - Tree1.Query(qus[i].R + 1, leny) - Tree2.Query(1, qus[i].L - 1) - 1;
else
degree[-qus[i].id] -= sum - Tree1.Query(qus[i].R + 1, leny) - Tree2.Query(1, qus[i].L - 1);
}
if ( add1[i] )
Tree1.Insert(add1[i], 1), ++sum;
if ( add2[i] )
Tree2.Insert(add2[i], 1);
}
for ( int i = 1; i <= lenx; i ++ )
qus[i] = Question(0, 0, 0);
for ( int i = 1; i <= n; i ++ )
qus[matrix[i].X2] = Question(matrix[i].Y1, matrix[i].Y2, -matrix[i].id);
for ( int i = 1; i <= n; i ++ )
qus[matrix[i].X1] = Question(matrix[i].Y1, matrix[i].Y2, matrix[i].id);
Tree3.Build(1, 1, leny);
for ( int i = 1; i <= lenx; i ++ )
{
if ( qus[i].id )
{
if ( qus[i].id > 0 )
{
cnt -= Tree3.Query(0) - Tree3.Query(1);
Tree3.Insert(1, 1, leny, qus[i].L, qus[i].R, 1, 0);
Tree3.Insert(1, 1, leny, qus[i].L, qus[i].R - 1, 1, 1);
cnt += Tree3.Query(0) - Tree3.Query(1);
}
else
{
Tree3.Insert(1, 1, leny, qus[i].L, qus[i].R, -1, 0);
Tree3.Insert(1, 1, leny, qus[i].L, qus[i].R - 1, -1, 1);
}
}
}
for ( int i = 1; i <= n; i ++ )
ans += 1LL * degree[i] * ( n - degree[i] - 1 );
ans = C(n, 3) - (ans >> 1) - cnt;
printf("%lld\n", ans);
return 0;
}
标签:Insert,qus,星穹,int,线段,铁道,崩坏,now,sum
From: https://www.cnblogs.com/KafuuChinocpp/p/17497163.html