掌中之物,未必在掌握之中。|

2021hych

园龄:2年7个月粉丝:2关注:2

P9478 [NOI2023] 方格染色题解

image

题解

对于行操作,列操作和对角线操作,实际上仅仅只是在对若干个矩形求面积并而已,这是裸的扫描线题,套用模板即可,此时注意到对角线操作实际上是 O(n) 量级的矩阵面积并,因此复杂度是 O(nlogq+qlogq) 的量级,只能获得 95 pts。

显然,面积并具有交换性,我们先做 O(qlogq) 的行与列矩阵的面积并,最后再看对角线,如果将对角线拆成 O(n) 量级的矩阵,不难发现在做面积并的时候,单位矩阵要么是被包含了,要么是独立的,看上去其实并不需要用线段树来维护,因而我们把对角线拆了再用线段树浪费了很多时间。再看到对角线的数量很少(在 95 pts 的做法中对角线操作的复杂度其实就考虑了这个事情),我们看看是否可以做一个简单的容斥?

在做完 O(qlogq) 的行与列矩阵的面积并后,我们考虑加入一条对角线,直接加 x2x1+1,那么我们必然会多统计一些点,怎么减掉呢?注意到,一条对角线与一个行矩阵或列矩阵最多只有一个交点,也就是说,多统计的点数是 O(q) 量级的,我们暴力的求解每一个行矩阵或列矩阵与对角线的交点数,如果这个复杂度对单次判断是 O(1) 的,那么我们就有了一个 O(q+qlogq) 的一个满分算法。

显然,这是 O(1) 的,本质上是在求一条一次函数与常函数的交点,只不过两个函数的定义域都有一定限制,我们只需要判断交点是否都在两个定义域之间即可。

但是我们仍然有可能多统计,具体有两方面,第一个,对角线之间可能重叠,我们可以在一开始就对对角线进行一次面积并,但我们仍然不需要线段树,由于本题对角线同向,两条线相交前提是两条线共线,只要截距相同,然后变成了可以 O(1) 解决的区间并,这里还是注意到对角线的数量是常数量级。第二个,一条对角线可能与多个行或列矩阵交于同一点,我们只需要减一次就够了,我们开一个 map 来标记已容斥过的点即可。

正确性和效率都得到了保证,可以开始码了,这里使用了动态开点的线段树,省去离散化的过程了。

AC code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e6+10;
int c,n,m,q,T,X1,Y1,X2,Y2,tot,len,cnt,sz,rt;
int ans,flag[N];
map<pair<int,int>,int>Map;
struct scan {
	int x,up,down,ty;
}L[N];
bool cmp(scan a,scan b) {
	return a.x<b.x;
}
struct Line {
	int X1,Y1,X2,Y2;
}L12[N],L3[N];
bool check1(Line a,Line b) {
	if(a.X1-a.Y1!=b.X1-b.Y1) return false;
	if(a.X2<b.X1||a.X1>b.X2) return false;
	return true;
}
bool check2(Line a,Line b,int op) {
	if(op==1) {
		int X=b.X1-b.Y1+a.Y1;
		if(Map[make_pair(X,a.Y1)]) return false;
		if(a.X1<=X&&X<=a.X2&&b.X1<=X&&X<=b.X2) {
			Map[make_pair(X,a.Y1)]=1;
			return true;
		}
		return false;
	}
	else {
		int Y=b.Y1-b.X1+a.X1;
		if(Map[make_pair(a.X1,Y)]) return false;
		if(a.Y1<=Y&&Y<=a.Y2&&b.Y1<=Y&&Y<=b.Y2) {
			Map[make_pair(a.X1,Y)]=1;
			return true;
		}
		return false;
	}
}
struct node {
	int ls,rs,len,cov;
}t[N<<2];
void pushup(int p,int L,int R) {
	if(t[p].cov) t[p].len=R-L+1;
	else t[p].len=t[t[p].ls].len+t[t[p].rs].len;
}
void update(int &p,int L,int R,int l,int r,int d) {
	if(!p) p=++sz;
	if(l<=L&&R<=r) {
		t[p].cov+=d;
		pushup(p,L,R);
		return;
	}
	int mid=L+R>>1;
	if(l<=mid) update(t[p].ls,L,mid,l,r,d);
	if(r>mid) update(t[p].rs,mid+1,R,l,r,d);
	pushup(p,L,R);
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>c>>n>>m>>q;
	for(int i=1;i<=q;i++) {
		cin>>T>>X1>>Y1>>X2>>Y2;
		if(T==1||T==2) {
			L[++tot].x=X1,L[tot].up=Y2,L[tot].down=Y1,L[tot].ty=1;
			L[++tot].x=X2+1,L[tot].up=Y2,L[tot].down=Y1,L[tot].ty=-1;
			L12[++cnt].X1=X1,L12[cnt].Y1=Y1,L12[cnt].X2=X2,L12[cnt].Y2=Y2;
		}
		if(T==3) L3[++len].X1=X1,L3[len].Y1=Y1,L3[len].X2=X2,L3[len].Y2=Y2;
	}
	sort(L+1,L+tot+1,cmp);
	for(int i=1;i<tot;i++) {
		update(rt,1,m,L[i].down,L[i].up,L[i].ty);
		ans+=t[rt].len*(L[i+1].x-L[i].x);
	}
	for(int i=1;i<len;i++) {
		if(!flag[i]) {
			for(int j=i+1;j<=len;j++) {
				if(!flag[j]&&check1(L3[i],L3[j])) {
					L3[i].X1=min(L3[i].X1,L3[j].X1);
					L3[i].Y1=min(L3[i].Y1,L3[j].Y1);
					L3[i].X2=max(L3[i].X2,L3[j].X2);
					L3[i].Y2=max(L3[i].Y2,L3[j].Y2);
					flag[j]=1;
				}
			}
		}
	}
	for(int i=1;i<=len;i++) {
		if(flag[i]) continue;
		ans+=L3[i].X2-L3[i].X1+1;
		for(int j=1;j<=cnt;j++) {
			if(L12[j].Y1==L12[j].Y2) {
				if(check2(L12[j],L3[i],1)) ans--;
			}
			if(L12[j].X1==L12[j].X2){
				if(check2(L12[j],L3[i],2)) ans--;
			}
		}
	}
	cout<<ans;
	return 0;
}

本文作者:2021hych

本文链接:https://www.cnblogs.com/2021hych/p/18012498

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   2021hych  阅读(68)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起