【cdoj 1335】 郭大侠与“有何贵干?” 线段树+扫描线

OK,现在来填这个坑。题目虽然说是一个需要维护的三维坐标,但是由于z巨小(1->3)所以直接用二维的扫描线就好了,然后把长方体的高拆了,就变成了求两次扫描线求两次面积再相加就好了(具体可以看代码)。这里由于需要计算被覆盖k次的矩阵的面积,所以需要用线段树来维护当前扫描线被覆盖k次的区域的长度,然后每次向上找新的扫描线的时候就用    △h * 覆盖k次的区域长度

1.线段树维护的扫描线是很多段区间,共同组成一条扫描线

2.每次更新 △h 用pre存储上一次的 纵坐标y

3.横坐标需要离散化

4.把每一条新加入的线段看成一条扫描线,所以可能会有高度相同的扫描线但是最终并不影响答案


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ls u<<1,l,mid
#define rs u<<1|1,mid,r
#define maxn 100020
#define ll long long
using namespace std;
int n , k;
struct Line{
	int lx,rx,y,flag;
	bool operator<(const Line& b)const{return y<b.y;}
	void join(int llx,int rrx,int yy,int fflag){
		lx=llx,rx=rrx,y=yy,flag=fflag;//初始化 
	}
}linea[maxn*4],lineb[maxn*4];//a是第一层 b是第二层
int cnta,cntb;//a的线段数 b的线段数 

struct node{
	int l,r,sum[15],cover;//sum[i]表示覆盖i次的扫描线长度
	void clear(){
		memset(sum,0,sizeof(sum));cover=0;l=r=0;
	}
}nod[maxn*8];

int cur[maxn*8],cnt;//用于离散化 横坐标
bool cmp(int a,int b){return a<b;}

ll ans;

void build(int u,int l,int r){
	nod[u].clear();
	nod[u].l=l;nod[u].r=r;
	nod[u].sum[0]=cur[r]-cur[l];
	if(l==r-1){
		return;
	}
	int mid=(l+r)>>1;
	build(ls);
	build(rs);
}


void push_up(int u){
	int l=nod[u].l,r=nod[u].r;
	memset(nod[u].sum,0,sizeof(nod[u].sum));
	if(l==r-1){
		nod[u].sum[min(k+1,nod[u].cover)]=cur[r]-cur[l];
	}else{
		for(int i=0;i<=k+1;i++)
			nod[u].sum[min(k+1,i+nod[u].cover)]=nod[u<<1].sum[i]+nod[u<<1|1].sum[i];
	}
}

void updata(int u,int l,int r,int x,int y,int add){
	if(x==y)return;
	if(l>=x&&r<=y){ 
		nod[u].cover+=add;
	}
	else{
		int mid=(l+r)>>1;
		if(y>mid)updata(rs,x,y,add);
		if(x<mid)updata(ls,x,y,add);
	}
	push_up(u);
}
void solve(){
	build(1,0,cnt);
	int pre=linea[1].y;//每一条线段作为一条扫描线,计算两条扫描线之间的覆盖k次的面积 
	for(int i=1;i<=cnta;i++){
		ans+=(ll)(linea[i].y-pre)*nod[1].sum[k];
		updata(1,0,cnt,linea[i].lx,linea[i].rx,linea[i].flag);
		pre=linea[i].y;
	}
	build(1,0,cnt);
	pre=lineb[1].y;//每一条线段作为一条扫描线,计算两条扫描线之间的覆盖k次的面积 
	for(int i=1;i<=cntb;i++){
		ans+=(ll)(lineb[i].y-pre)*nod[1].sum[k];
		updata(1,0,cnt,lineb[i].lx,lineb[i].rx,lineb[i].flag);
		pre=lineb[i].y;
	}
}
int main(){
	scanf("%d%d",&n,&k);
	int x1,y1,z1,x2,y2,z2;
	for(int i=1;i<=n;i++){
		scanf("%d%d%d%d%d%d",&x1,&y1,&z1,&x2,&y2,&z2);
		cur[++cnt]=x1;cur[++cnt]=x2;
		if(z1==1&&z2==2){//加入线段(可能有错) 
			linea[++cnta].join(x1,x2,y1,1);
			linea[++cnta].join(x1,x2,y2,-1);
		}
		if(z1==1&&z2==3){
			linea[++cnta].join(x1,x2,y1,1);
			linea[++cnta].join(x1,x2,y2,-1);
			lineb[++cntb].join(x1,x2,y1,1);
			lineb[++cntb].join(x1,x2,y2,-1);
		}
		if(z1==2&&z2==3){
			lineb[++cntb].join(x1,x2,y1,1);
			lineb[++cntb].join(x1,x2,y2,-1);
		}
	}
	sort(linea+1,linea+1+cnta);
	sort(lineb+1,lineb+1+cntb);
	sort(cur+1,cur+cnt+1,cmp);
	cnt=unique(cur+1,cur+cnt+1)-cur-1;
	for(int i=1;i<=cnta;i++){//把原来的线段的坐标换上显得编号 
		linea[i].lx=lower_bound(cur+1,cur+1+cnt,linea[i].lx)-cur;
		linea[i].rx=lower_bound(cur+1,cur+1+cnt,linea[i].rx)-cur;
	}
	for(int i=1;i<=cntb;i++){//把原来的线段的坐标换上显得编号 
		lineb[i].lx=lower_bound(cur+1,cur+1+cnt,lineb[i].lx)-cur;
		lineb[i].rx=lower_bound(cur+1,cur+1+cnt,lineb[i].rx)-cur;
	}
	solve();//解决问题 
	printf("%lld",ans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值