1. 程式人生 > 其它 >[COCI2018-2019#2] Sunčanje 題解

[COCI2018-2019#2] Sunčanje 題解

link

考慮簡潔的 \(\text{cdq}\) 做法。

約定:第 \(i\) 個矩形左下角為 \((xl_i,yl_i)\),右上角為 \((xr_i,yr_i)\),所有座標都已離散化。

如果 第 \(i\) 個矩形可以覆蓋第 \(j\) 個矩形,那麼必須滿足:

  • \(i>j\)

  • \(xl_i\le xr_j,xr_i\ge xl_j\)(橫向有交點)

  • \(yl_i\le yr_j,yr_i\ge yl_j\)(豎向有交點)

首先將矩形按照出現的順序降序排列,解決完第一個條件。

考慮滿足 \(xl_i\le xr_j\)。將 \([l,mid]\) 按照 \(xl\)

排序,\([mid+1,r]\) 按照 \(xr\) 排序,然後將滿足 \(xl_i\le xr_j\) 條件的矩形存入線段樹中(具體存法後文講),這樣線上段樹中的矩形都隨時滿足 \(xl_i\le xr_j\)

關鍵在於如何將矩形儲存到線段樹中來滿足剩下三個條件。

將線段樹看做縱座標的值域,那麼儲存第 \(i\) 個矩形就一定是對區間 \([yl_i,yr_i]\) 進行操作,且操作的值一定和 \(xr_i\) 有關。

看到剩下的條件 \(xr_i\ge xl_j\),發現 \(xr_i\) 越大越好,於是儲存矩形到線段樹中的操作就很明瞭了,即 \([yl_i,yr_i]\) 更新最大值 \(xr_i\)

\(f_j\) 表示第 \(j\) 個矩形是否被覆蓋,線段樹區間最大值為 \(g_{[l,r]}\),那麼就有:

\[f_j=f_j|[g_{[yl_j,yr_j]}\ge xl_j] \]

所以執行一遍 \(\text{cdq}\) 就全部搞定了。

關於線段樹清空

在多設一個區間是否清空的懶標記,每次懶標記下傳的時候,先下穿清空的懶標記,再下傳正常的懶標記。

程式碼:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=200010;
const int N=(maxn<<2);
inline int read(){
	int x=0;
	char c=getchar();
	for(;!(c>='0'&&c<='9');c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return x;
}
struct node{
	int id,xl,yl,xr,yr;
}a[maxn];
int data[N],lz[N],LZ[N];
//data為區間最值,lz為正常懶標記,LZ為清空懶標記
int X[N],Y[N];
int n,m;
bool f[maxn];
bool cmp1(node a,node b){
	return a.xl<b.xl;
}
bool cmp2(node a,node b){
	return a.xr<b.xr;
}
bool cmp3(node a,node b){
	return a.id<b.id;
}
bool cmp4(node a,node b){
	return a.id>b.id;
}
void pushdown(int i){
	if(~LZ[i]){
	//先下傳清空懶標記(可以思考下傳遞順序的問題)
		lz[i<<1]=lz[i<<1|1]=0;
		data[i<<1]=data[i<<1|1]=0;
		LZ[i<<1]=LZ[i<<1|1]=0;
		LZ[i]=-1;
	} 
	lz[i<<1]=max(lz[i<<1],lz[i]);
	lz[i<<1|1]=max(lz[i<<1|1],lz[i]);
	data[i<<1]=max(data[i<<1],lz[i]);
	data[i<<1|1]=max(data[i<<1|1],lz[i]);
	lz[i]=0;
}
void add(int i,int l,int r,int L,int R,int x){
	if(l>R||r<L) return ;
	if(l>=L&&r<=R){
		data[i]=max(data[i],x);
		lz[i]=max(lz[i],x);
		return ;
	}
	pushdown(i);
	int mid=l+r>>1;
	add(i<<1,l,mid,L,R,x);
	add(i<<1|1,mid+1,r,L,R,x);
	data[i]=max(data[i<<1],data[i<<1|1]);
}
int Query(int i,int l,int r,int L,int R){
	if(l>R||r<L) return 0;
	if(l>=L&&r<=R) return data[i];
	pushdown(i);
	int mid=l+r>>1;
	return max(Query(i<<1,l,mid,L,R),Query(i<<1|1,mid+1,r,L,R));
}
void cdq(int l,int r){
	if(r-l<1) return ;
	int mid=l+r>>1,ii=l,x;
	cdq(l,mid),cdq(mid+1,r);
	sort(a+l,a+1+mid,cmp1);
	sort(a+mid+1,a+r+1,cmp2);
	//注意[l,mid][mid+1,r]排序方法不同
	for(int j=mid+1;j<=r;j++){//cdq常規操作
		while(ii<=mid&&a[ii].xl<=a[j].xr) 
			add(1,1,m,a[ii].yl,a[ii].yr,a[ii].xr),ii++;
		f[a[j].id]|=(Query(1,1,m,a[j].yl,a[j].yr)>=a[j].xl);
		//更新fj
	}
	data[1]=lz[1]=LZ[1]=0,pushdown(1);
	//將整個區間打上清空懶標記
	//不要忘記下傳一層 
}
map<int,int>Hx,Hy;
int main(){
	n=read(),memset(LZ,-1,sizeof(LZ));
	int x,y,u,v,cntx=0,cnty=0,Cntx=0,Cnty=0;
	for(int i=1;i<=n;i++){
		a[i].id=i,x=read()+1,y=read()+1,u=read(),v=read();
		a[i].xl=x,a[i].yl=y,a[i].xr=x+u-1,a[i].yr=y+v-1;
		X[++cntx]=a[i].xl,X[++cntx]=a[i].xr;
		Y[++cnty]=a[i].yl,Y[++cnty]=a[i].yr;
	}
	sort(X+1,X+1+cntx),sort(Y+1,Y+1+cnty);
	Hx[X[1]]=++Cntx,Hy[Y[1]]=++Cnty;
	for(int i=2;i<=cntx;i++){
		if(X[i]!=X[i-1]) Hx[X[i]]=++Cntx;
		if(Y[i]!=Y[i-1]) Hy[Y[i]]=++Cnty;
	}
	for(int i=1;i<=n;i++){
	//離散化(其實xl,xr是不用離散化的)
		a[i].xl=Hx[a[i].xl],a[i].xr=Hx[a[i].xr];
		a[i].yl=Hy[a[i].yl],a[i].yr=Hy[a[i].yr];
	} 
	sort(a+1,a+1+n,cmp4),m=Cnty,cdq(1,n);
	for(int i=1;i<=n;i++) 
		printf("%s\n",f[i]?"NE":"DA");
	return 0;
}

\(\text{AC}\)