题解
对于行操作,列操作和对角线操作,实际上仅仅只是在对若干个矩形求面积并而已,这是裸的扫描线题,套用模板即可,此时注意到对角线操作实际上是 \(O(n)\) 量级的矩阵面积并,因此复杂度是 \(O(n\log q+q\log q)\) 的量级,只能获得 95 pts。
显然,面积并具有交换性,我们先做 \(O(q\log q)\) 的行与列矩阵的面积并,最后再看对角线,如果将对角线拆成 \(O(n)\) 量级的矩阵,不难发现在做面积并的时候,单位矩阵要么是被包含了,要么是独立的,看上去其实并不需要用线段树来维护,因而我们把对角线拆了再用线段树浪费了很多时间。再看到对角线的数量很少(在 95 pts 的做法中对角线操作的复杂度其实就考虑了这个事情),我们看看是否可以做一个简单的容斥?
在做完 \(O(q\log q)\) 的行与列矩阵的面积并后,我们考虑加入一条对角线,直接加 \(x_2-x_1+1\),那么我们必然会多统计一些点,怎么减掉呢?注意到,一条对角线与一个行矩阵或列矩阵最多只有一个交点,也就是说,多统计的点数是 \(O(q)\) 量级的,我们暴力的求解每一个行矩阵或列矩阵与对角线的交点数,如果这个复杂度对单次判断是 \(O(1)\) 的,那么我们就有了一个 \(O(q+q\log q)\) 的一个满分算法。
显然,这是 \(O(1)\) 的,本质上是在求一条一次函数与常函数的交点,只不过两个函数的定义域都有一定限制,我们只需要判断交点是否都在两个定义域之间即可。
但是我们仍然有可能多统计,具体有两方面,第一个,对角线之间可能重叠,我们可以在一开始就对对角线进行一次面积并,但我们仍然不需要线段树,由于本题对角线同向,两条线相交前提是两条线共线,只要截距相同,然后变成了可以 \(O(1)\) 解决的区间并,这里还是注意到对角线的数量是常数量级。第二个,一条对角线可能与多个行或列矩阵交于同一点,我们只需要减一次就够了,我们开一个 \(\text{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;
}
标签:int,题解,tot,X2,NOI2023,P9478,对角线,Y1,X1
From: https://www.cnblogs.com/2021hych/p/18012498