CF243D - Cubes

首先我们通过翻转坐标系强制要求光的方向是正方向。翻转坐标系就翻转矩阵的对应维度就可以了。

然后我们发现,如果不考虑遮挡关系,只考虑平面,对于每个位置的方块而言,能照到它的光是一段区间。就是能照到 \((i-1,j)\) 和能找到 \((i,j-1)\) 的光。

我们考虑把光映射到光所在直线在 \(y\) 轴上的投影。那么经过点 \((x,y)\) 的光就映射到 \(y-\dfrac{v_yx}{v_x}\),我们就映射到了 \(y\) 轴的一个区间 \([l_{x,y},r_{x,y}]\),只要这个区间里有光,当前位置就能被照到。

同时我们容易发现,每个方块所对应的区间是等长的。不过这不重要。

我们把所有的光区间端点离散化下来。然后对于 \((x,y)\) 的位置来说,只要在当前高度 \([l_{x,y},r_{x,y}]\) 上任意的点有光就可以。而对于高度而言有没有光显然是存在单调性的。

同时遮挡关系也是存在单调性的。处在当前点右边或者上方的点一定遮挡不了当前的点。 那么我们考虑从左下角一行一行扫,开一棵线段树记录,对于每个光区间,有多少高度已经完全没有光了。

这样,我们每次在线段树上查询区间最小值(只要有即可),找到当前的位置有多少方块被挡住了,用 \(a_{i,j}\) 减得到没挡住的。然后区间修改,自己区间上的所有数对 \(a_{i,j}\)\(\max\)(这些光都被遮挡了)。

所以,我们只要实现线段树区间取 \(\max\),查询区间最小值,这是容易的。

同时注意,这题卡精度,我的方法是实现了一个 struct,然后重载了小于号,这样的好处是依旧可以使用 sortlower_bound 而不掉精度。离散化的时候要 unique,否则线段树区间会出问题,这个功能我不知道要重载什么,但是可以直接自己手写。

复杂度 \(O(n^2\log n)\)

const ld eps=1e-6;
int n,a[1005][1005],vx,vy,m; 
int l[1005][1005],r[1005][1005];
struct mydouble{
	ld x;
	bool operator ==(const mydouble b)const{
		return fabs(x-b.x)<=eps;
	}
	bool operator <(const mydouble b)const{
		return b.x>x&&!(fabs(b.x-x)<=eps);
	}
};
mydouble L[1005][1005],R[1005][1005],x[2000005],y[2000005];
struct node{
	int l,r,m,tg;
}sg[8000005];
inline void init(int i,int l,int r){
	sg[i].l=l,sg[i].r=r,sg[i].m=0,sg[i].tg=0;
	if(l==r)return;
	init(i<<1,l,(l+r)>>1);
	init(i<<1|1,((l+r)>>1)+1,r);
}
inline void push(int i){
	if(!sg[i].tg)return;
	sg[i].m=max(sg[i].m,sg[i].tg);
	if(sg[i].l!=sg[i].r){
		sg[i<<1].tg=max(sg[i<<1].tg,sg[i].tg);
		sg[i<<1|1].tg=max(sg[i<<1|1].tg,sg[i].tg);
	}sg[i].tg=0;
}
inline void modify(int i,int l,int r,int x){
	push(i);
	if(sg[i].l>r||sg[i].r<l)return;
	if(sg[i].l>=l&&sg[i].r<=r){
		sg[i].tg=max(sg[i].tg,x);
		return push(i);
	}modify(i<<1,l,r,x);
	modify(i<<1|1,l,r,x);
	sg[i].m=min(sg[i<<1].m,sg[i<<1|1].m);
}
inline int qry(int i,int l,int r){
	push(i);
	if(sg[i].l>r||sg[i].r<l)return 1e9;
	if(sg[i].l>=l&&sg[i].r<=r)return sg[i].m;
	int res=min(qry(i<<1,l,r),qry(i<<1|1,l,r));
	sg[i].m=min(sg[i<<1].m,sg[i<<1|1].m);
	return res;
}
inline void chdir(){
	if(vx<0){
		rep(j,1,n)rep(i,1,n/2)swap(a[i][j],a[n-i+1][j]);
		vx=-vx;
	}
	if(vy<0){
		rep(i,1,n)rep(j,1,n/2)swap(a[i][j],a[i][n-j+1]);
		vy=-vy;
	}
}
inline ld calc(int x,int y){
	return y-1.0*x*vy/vx;
}
inline void calclr(){
	rp(i,n)rp(j,n){
		if(vx!=0){
			L[i][j].x=calc(i,j-1);
			R[i][j].x=calc(i-1,j);
			x[++m]=L[i][j];
			x[++m]=R[i][j];
		}else{
			L[i][j].x=i-1,R[i][j].x=i;
			x[++m].x=i-1,x[++m].x=i;
		}
	}
	sort(x+1,x+1+m);
	int cnt=1;y[cnt]=x[1];
	rep(i,2,m)if(!(x[i-1]==x[i]))y[++cnt]=x[i];
	m=cnt;
	rep(i,2,m)x[i]=y[i];
	rp(i,n)rp(j,n){
		l[i][j]=lower_bound(x+1,x+1+m,L[i][j])-x;
		r[i][j]=lower_bound(x+1,x+1+m,R[i][j])-x-1;
	}init(1,1,m);
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>vx>>vy;
	rp(i,n)rp(j,n)cin>>a[i][j];
	chdir();
	calclr();
	ll ans=0;
	rp(i,n)rp(j,n)if(a[i][j]){
		ans+=max(0,a[i][j]-qry(1,l[i][j],r[i][j]));
		modify(1,l[i][j],r[i][j],a[i][j]);
	}cout<<ans<<endl;
	return 0;
}
//Crayan_r
posted @ 2023-05-15 21:23  jucason_xu  阅读(15)  评论(0编辑  收藏  举报