CCPC2023 河南省赛 数正方形

发布时间 2023-05-08 22:34:20作者: autoint

数正方形

给出一个 (2n + 1) × (2n + 1) 的网格图,左下角的坐标为 (0, 0),右上角的坐标为 (2n + 1, 2n + 1)。

在这个网格图中有 n 个矩形,第 i 个矩形左下角的坐标为 (x1i, y1i),右上角的坐标为 (x2i, y2i),且序列{x11, x12, . . . , x1n, x21, x22, . . . , x2n} 和序列 {y11, y12, . . . , y1n, y21, y22, . . . , y2n} 分别都是 1 ∼ 2n 的一个排列。

我们按照以下方法对这 (2n + 1) × (2n + 1) 个格子进行黑白染色:被矩形覆盖了偶数次的格子染上白色,否则染上黑色。

求格子中有多少个 2 × 2 的正方形是纯色的,即仅由白色格子或黑色格子构成的正方形。

\(1 ≤ n ≤ 10^5\)

题解

按照方块纵坐标组合是(1, 2), (2, 3), ...的顺序统计纯色方块个数。

这个线段树操作比较有意思。扫描线上需要翻转的部分就翻转,不需要翻转的部分是可能产生纯色正方形的,需要统计相同颜色段的长度-1的和,可以用线段树维护。

上下两头需要特殊处理。

时间复杂度O(n log n)

constexpr int N=2e5+10;
vector<pair<int, int> > e[N];

struct Node{
	bool l, r;
	int sum;
};

Node operator+(const Node& a, const Node& b){
	return {a.l, b.r, a.sum+b.sum+(a.r==b.l)};
}
Node operator~(const Node& a){
	return {!a.l, !a.r, a.sum};
}

Node tree[4*N];
bool tag[4*N];

#define lc (x<<1)
#define rc (x<<1|1)
#define mid ((l+r)>>1)

void pushDown(int x){
	if(tag[x]){
		tree[lc]=~tree[lc], tag[lc]^=1;
		tree[rc]=~tree[rc], tag[rc]^=1;
		tag[x]=0;
	}
}
void build(int x, int l, int r){
	if(l==r){
		tree[x]={0, 0, 0}, tag[x]=0;
		return;
	}
	build(lc, l, mid);
	build(rc, mid+1, r);
	tree[x]=tree[lc]+tree[rc], tag[x]=0;
}
void modify(int x, int l, int r, int ql, int qr){
	if(ql<=l && r<=qr){
		tree[x]=~tree[x], tag[x]^=1;
		return;
	}
	pushDown(x);
	if(ql<=mid) modify(lc, l, mid, ql, qr);
	if(qr>mid) modify(rc, mid+1, r, ql, qr);
	tree[x]=tree[lc]+tree[rc];
}
Node query(int x, int l, int r, int ql, int qr){
	if(ql<=l && r<=qr) return tree[x];
	pushDown(x);
	if(qr<=mid) return query(lc, l, mid, ql, qr);
	if(ql>mid) return query(rc, mid+1, r, ql, qr);
	return query(lc, l, mid, ql, qr)+query(rc, mid+1, r, ql, qr);
}
	
int main(){
	int n=read<int>();
	for(int i=1; i<=n; ++i){
		int x1=read<int>(), y1=read<int>(), x2=read<int>(), y2=read<int>();
		e[x1].push_back({y1, y2});
		e[x2].push_back({y1, y2});
	}
	build(1, 1, 2*n-1);
	int64 ans=0;
	for(int p=1; p<=2*n; ++p){
		vector<pair<int, int> > a;
		bool top=0, bottom=0;
		for(const pair<int, int>& t: e[p]){
			a.push_back({t.first, 1});
			if(t.first==1) top^=1;
			a.push_back({t.second, -1});
			if(t.second==2*n) bottom^=1;
		}
		a.push_back({1, 0});
		a.push_back({2*n, 0});
		sort(a.begin(), a.end());
		bool sum=0;
//		cerr<<"p="<<p<<endl;
		for(int l=0, r; l<(int)a.size(); l=r+1){
			for(r=l; r+1<(int)a.size() && a[r+1].first==a[l].first; ++r);
			for(int i=l; i<=r; ++i) sum+=a[i].second;
			if(a[l].first==2*n) break;
//			cerr<<"    "<<a[l].first<<" "<<a[r+1].first-1<<" sum="<<sum<<endl;
			if(sum%2) modify(1, 1, 2*n-1, a[l].first, a[r+1].first-1);
			else ans+=query(1, 1, 2*n-1, a[l].first, a[r+1].first-1).sum;
		}
		ans+=!top && !tree[1].l;
		ans+=!bottom && !tree[1].r;
	}
	printf("%lld\n", ans);
	return 0;
}