铺地毯
- problem
- solution
- code
problem
给定矩阵的长宽 P,QP,QP,Q,矩阵从下往上从左往后编号增加,(0,0)∼(P,Q)(0,0)\sim (P,Q)(0,0)∼(P,Q)。
给定 nnn 张长宽平行于坐标轴的矩形地毯,左下角和右上角的坐标。
求被至少 n−1n-1n−1 张地毯覆盖到的格子数量。
n≤3e5,P,Q≤1e9n\le 3e5,P,Q\le 1e9n≤3e5,P,Q≤1e9。
solution
矩形欸!算的也是矩形相关的覆盖面积。
树套树 nlog2nn\log^2nnlog2n,扫描线 nlognn\log nnlogn。笑死,根本写不动。
observation1
:任何矩形的交最后一定也是平行于坐标轴的矩形或为空。
observation2
:矩形的交不可逆,即不能将所有矩形交起来再去掉其中一个得到 n−1n-1n−1 个矩形的交。
对于矩阵序列计算前缀交和后缀交,然后排除掉第 iii 个矩形。
即算 [1,i)⋂(i,n][1,i)\bigcap(i,n][1,i)⋂(i,n] 的矩形的交,然后计算给答案。
这样就计算了每 n−1n-1n−1 个矩形的交。
但是如果某些部分是 nnn 个矩形的交,就会被计算 nnn 次,最后去重一下减掉 n−1n-1n−1 次的计算结果即可。
code
#include <bits/stdc++.h>
using namespace std;
#define maxn 300005
int P, Q, n, T;
struct node {int l1, r1, l2, r2;friend node cross( node x, node y ) {return { max( x.l1, y.l1 ), max( x.r1, y.r1 ), min( x.l2, y.l2 ), min( x.r2, y.r2 ) };}long long calc() { if( l1 > l2 or r1 > r2 ) return 0;else return 1ll * ( l2 - l1 ) * ( r2 - r1 );}
}matrix[maxn], pre[maxn], suf[maxn];int main() {freopen( "carpet.in", "r", stdin );freopen( "carpet.out", "w", stdout );scanf( "%d", &T );while( T -- ) {scanf( "%d %d %d", &P, &Q, &n );for( int i = 1, l1, r1, l2, r2;i <= n;i ++ ) {scanf( "%d %d %d %d", &l1, &r1, &l2, &r2 );matrix[i] = { l1, r1, l2, r2 };}if( n == 1 ) { printf( "%lld\n", matrix[1].calc() ); continue; }long long ans = 0;pre[1] = matrix[1], suf[n] = matrix[n];for( int i = 2;i <= n;i ++ ) pre[i] = cross( pre[i - 1], matrix[i] );for( int i = n - 1;i;i -- ) suf[i] = cross( suf[i + 1], matrix[i] );ans += pre[n - 1].calc();ans += suf[2].calc();for( int i = 2;i < n;i ++ ) ans += cross( pre[i - 1], suf[i + 1] ).calc();ans -= ( n - 1 ) * pre[n].calc();printf( "%lld\n", ans );}return 0;
}