D. Karen and Cards(思维)

本文探讨了一种解决三维空间中特定条件下的点计数问题的算法策略。通过定义一个三维区域,目标是找出该区域内的点,这些点在至少两个维度上的坐标超过已知点集中的任一点。算法采用逆向思维,首先计算不满足条件的点数量,再从总点数中减去这一数量以得到最终答案。具体实现涉及点的排序、单调栈处理和区域并集计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

三维空间的n个点 p [ 1 ] , p [ 2 ] , . . . p [ n ] p[1],p[2],...p[n] p[1],p[2],...p[n],给定区域 1 < = x < = p , 1 < = y < = q , 1 < = z < = r 1<=x<=p, 1<=y<=q, 1<=z<=r 1<=x<=p,1<=y<=q,1<=z<=r,问在区域内有多少个点 ( x , y , z ) (x,y,z) (x,y,z)满足对于n个点中任意一个点p[i],至少都有两个维度的坐标比p[i]大。

思路

正着求不是很好算,就求不能被有多少个 ( x , y , z ) (x,y,z) (x,y,z)满足n个点中至少存在一点p[i],使得p[i]至少有两个维度的坐标小于等于点 ( x , y , z ) (x,y,z) (x,y,z)。那么可以枚举一个维度去计算平面上的满足情况。
将n个点按z排序,枚举z从1到r,假设当z = c时候, p [ 1 ] , p [ 2 ] . . p [ k ] p[1],p[2]..p[k] p[1],p[2]..p[k]的z坐标都小于c, p [ k + 1 ] , . . . p [ n ] p[k + 1],...p[n] p[k+1],...p[n]z坐标大于等于c, p x = m a x ( p [ k + 1 ] . x , p [ k + 2 ] . x . . . . p [ n ] . x ) , p y = m a x ( p [ k + 1 ] . y , p [ k + 2 ] . y , . . . p [ n ] . y ) px = max(p[k + 1].x,p[k + 2].x....p[n].x),py = max(p[k + 1].y,p[k + 2].y,...p[n].y) px=max(p[k+1].x,p[k+2].x....p[n].x),py=max(p[k+1].y,p[k+2].y,...p[n].y),那么对于后半部分 [ k + 1 , n ] [k+1,n] [k+1,n]合法区域为整个平面区域扣掉以 ( p x + 1 , p y + 1 ) 为 左 下 角 , ( p , q ) (px + 1,py + 1)为左下角,(p,q) (px+1,py+1)(p,q)为右上角的矩形。对于前半部分 [ 1 , k ] [1,k] [1,k],对应k个矩形 ( 0 , 0 ) 为 左 下 角 , ( p [ i ] . x , p [ i ] . y ) 为 右 上 角 (0,0)为左下角,(p[i].x,p[i].y)为右上角 (0,0),(p[i].x,p[i].y),前半部分的合法区域就是这k个矩形的并,前半部分的区域再并上后半部分区域,就是当z = c时,不合法的情况。假设当z = r时候,所有矩形都在前半部分,后半部分为空,那么此时的区域面积大小可以用单调栈直接处理。当z往下递减的时候,相当于前半部分要去掉一个矩形,后半部分的区域用这个矩形更新一下,后半部分的区域实际上是一个不断扩大的区域,并且一定包含新加入的这个矩形,那么可以发现z = c的时候的所有情况一定都包含在 z = c-1的情况中,那么每次前半部分的区域实际上可以看成不变的就是n个矩形的并,直接提前预处理出来,然后每次并上后半部分的区域,而后半部分的区域是一个比较规整的区域(一个大矩形,挖掉右上角的一个矩形),对单调栈处理后的n个矩形并的区域,前后扣一扣就可以算出并集。从大到小枚举完z就可以计算出所有不合法的,全部扣掉不合法的就算完答案了。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,int>pii;
typedef vector<int>vi;

#define rep(i,a,b) for(int i=(a);i<(b);i++)
#define fi first
#define se second
#define de(x) cout<<#x<<"="<<x<<"\n"
#define dd(x) cout<<#x<<"="<<x<<" "
#define pb(x) push_back(x)
#define all(x) x.begin(),x.end()
#define sz(x) (int)x.size()
#define lowbit(a) ((a)&-(a))
#define per(i,a,b) for(int i=(b)-1;i>=(a);--i)
const int N=5e5+5;
const ll mod = 1e9 + 7;
int n , p , q , h;
struct node{
	int x,y,z,id;
}a[N],b[N];
bool cmp1(node a,node b){
	if(a.x != b.x)return a.x < b.x;
	return a.y < b.y;
}
bool cmp2(node a,node b){
	return a.z > b.z;
}
ll calc(ll x,ll y){
	return (ll)p*q - (p-x)*(q-y);
}
int st[N];
ll sum[N]; 
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	cin>>n>>p>>q>>h;
	rep( i , 1 , n + 1)
		cin>>a[i].x>>a[i].y>>a[i].z;
	sort(a + 1,a + n + 1,cmp1);
	int top = 0;
	rep( i , 1 , n + 1){
		while(top && a[st[top]].y <= a[i].y)top--;
		st[++top] = i;
	}
	ll un = 0,ans = 0;
	rep( i ,1 , top + 1){
		b[i] = a[st[i]];
	}
	rep( i , 1 , top + 1){
		un += (ll)(a[st[i]].x - a[st[i - 1]].x)*a[st[i]].y;
		sum[i] = un;
	}
	sort(a + 1,a + n + 1,cmp2); 
	int px =0,py = 0;
	int j = 1;
	int l = 1,r = top;
	for(int i = h;i >= 1;--i){
		while(j<=n && a[j].z >= i){
			px = max(px,a[j].x);
			py = max(py,a[j].y);
			j++;
		}
		while(l <= top && b[l].x <= px)l++;
		while(r >= 1 && b[r].y <= py)r--;
		if(px  == 0 && py == 0)
			ans += un;
		else{
			ll res = 0;
			if(l<=r){
				res = (sum[r] - sum[l - 1])-((ll)py*(b[r].x - px) + (ll)b[l].y*(px - b[l - 1].x)); 
			}
			ans += calc(px,py) + res;
		
		}
	} 
	ans = (ll)p*q*h - ans;
	cout<<ans<<endl;
	return 0;
	
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值