【GDKOI2010】推箱子

5 篇文章 0 订阅
1 篇文章 0 订阅

题目大意

给定 n n n 个矩形,每条边都平行与坐标轴的一条边,分别有 4 4 4 个量 x i , y i , L x i , L y i x_{i},y_{i},Lx_{i},Ly_{i} xi,yi,Lxi,Lyi x i , y i x_{i},y_{i} xi,yi 表示矩形左下角的点的坐标, L x i , L y i Lx_{i},Ly_{i} Lxi,Lyi 表示在横纵坐标的长度,保证两两矩形不重合,每次给定操作的矩形,表示将该矩形向左移动直至碰到坐标轴或其他矩形的边,保证一个矩形不会操作 2 2 2 次或以上,最后输出每个矩形的操作后左下点的坐标。

Input & Output

输入 n n n 表示 n n n 个矩形。
之后输入 n n n x i , y i , L x i , L y i x_{i},y_{i},Lx_{i},Ly_{i} xi,yi,Lxi,Lyi
最后输入 n n n 个数 a a a 表示移动第 i i i 个矩形。
输出 n n n 行表示第 i i i 行操作完后左下点坐标。

数据范围

0 ≤ x i , y i , L x i , L y i ≤ 2 31 − 1 , 0 ≤ n ≤ 1 0 5 0 \le x_{i},y_{i},Lx_{i},Ly_{i} \le 2^{31}-1,0 \le n \le 10^5 0xi,yi,Lxi,Lyi23110n105

思路

因为矩形移动时所受到影响的矩形时可以确定范围的,所以我们考虑预处理出来每一个矩形所能到达的其他矩形。
考虑按照横坐标从小到大将矩形排序,因为前面被覆盖的矩形肯定是不能影响后面矩形的,所以我们以纵坐标(要先离散化)建一颗线段树,区间 [ L , R ] [L,R] [L,R] 表示被覆盖的数是什么,有很多方法处理,我是将 m a x max max m i n min min 值存储起来,倘若区间的 m a x max max 值等于 m i n min min 值,则证明这个区间只有一种矩形覆盖掉,直接和当前矩形连一条边即可。
最后操作时枚举可到达的矩形求最右边的最大值即可。

AC代码

#include<cstring>
#include<iostream>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
struct node{
	int ax,ay,bx,by,id;
}d[200200];
struct T{
	int maxn,minn;
}tree[800800];
struct nod{
	int head,to,nxt;
}edge[4000200];
struct zlz{
	int val,id,k;
}yy[200200];
int n,cnt,lisan[200200],tag[800800],tot,a,to[200200];
void xx(int u,int v)
{
	edge[cnt].to=v;
	edge[cnt].nxt=edge[u].head;
	edge[u].head=cnt;
	cnt++;
}
void pushup(int k)
{
	tree[k].maxn=max(tree[k*2].maxn,tree[k*2+1].maxn);
	tree[k].minn=min(tree[k*2].minn,tree[k*2+1].minn);
	return;
}
void pushdown(int k)
{
	tree[k*2].maxn=tree[k*2].minn=tag[k];
	tree[k*2+1].maxn=tree[k*2+1].minn=tag[k];
	tag[k*2]=tag[k],tag[k*2+1]=tag[k];
	tag[k]=0;
}
void query(int k,int l,int r,int L,int R,int id)
{
	if(tree[k].maxn==0)
	return;
	if(tree[k].maxn==tree[k].minn&&L<=l&&r<=R)
	{
//		printf("!!!%d\n",tree[k].maxn);
		xx(id,tree[k].maxn);
		return;
	}
	int mid=(l+r)/2;
	if(tag[k])
	pushdown(k);
	if(R<=mid)
	query(k*2,l,mid,L,R,id);
	else if(L>mid)
	query(k*2+1,mid+1,r,L,R,id);
	else
	query(k*2,l,mid,L,mid,id),query(k*2+1,mid+1,r,mid+1,R,id);
}
void change(int k,int l,int r,int L,int R,int id)
{
	if(L<=l&&r<=R)
	{
		tree[k].maxn=id,tree[k].minn=id;
		tag[k]=id;
		return; 
	}
	int mid=(l+r)/2;
	if(tag[k])
	pushdown(k);
	if(R<=mid)
	change(k*2,l,mid,L,R,id);
	else if(L>mid)
	change(k*2+1,mid+1,r,L,R,id);
	else
	change(k*2,l,mid,L,mid,id),change(k*2+1,mid+1,r,mid+1,R,id);
	pushup(k);
}
bool cmp(node x,node y)
{
	return x.ax<y.ax;
}
bool cmp2(zlz x,zlz y)
{
	return x.val<y.val;
}
int main()
{
	memset(edge,-1,sizeof(edge));
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d %d %d %d",&d[i].ax,&d[i].ay,&d[i].bx,&d[i].by),d[i].bx+=d[i].ax,d[i].by+=d[i].ay,d[i].id=i;
		tot++,yy[tot].id=i,yy[tot].val=d[i].ay,yy[tot].k=0;
		tot++,yy[tot].id=i,yy[tot].val=d[i].by,yy[tot].k=1;
	}
	sort(yy+1,yy+tot+1,cmp2);
	int now=0;
	for(int i=1;i<=tot;i++)
	{
		if(i==1||yy[i].val!=yy[i-1].val)
		now++;
		if(yy[i].k==0)
		d[yy[i].id].ay=now;
		else
		d[yy[i].id].by=now;
	}	
	sort(d+1,d+n+1,cmp);
	for(int i=1;i<=n;i++)
	{
		to[d[i].id]=i;
//		printf("%d\n",i);
		query(1,1,tot,d[i].ay+1,d[i].by,i);
		change(1,1,tot,d[i].ay+1,d[i].by,i);
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a);
		a=to[a];
		int maxn=0;
		for(int j=edge[a].head;j!=-1;j=edge[j].nxt)
		{
			int y=edge[j].to;
			maxn=max(maxn,d[y].bx);
		}
		int len=d[a].bx-d[a].ax;
		d[a].ax=maxn,d[a].bx=d[a].ax+len;
	}
	for(int i=1;i<=n;i++)
	printf("%d\n",d[to[i]].ax);
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值