hdu 1828 && pku 1177 Picture

这困扰我N久的题目,终于过了。

求N个矩形的周长并,一开始觉得太遥不可及了,感觉好复杂 ,总想先搞懂计算的方法,再看大牛的方法,可是N久之后,对那个方法还是一知半解;

今天结合了代码还有下面俩副图之后,突然觉得豁然开朗啦,自己敲了一遍代码……

重点:我觉得,最重要的就是理解下面三个式子了,理解了之后,就知道该怎么建立线段树了;

   ans+=num[1]*(ss[i+1].x-ss[i].x);(横边)
   ans+=abs(len[1]-last);(竖边)
   last=len[1];

对所有的竖边按照x值从小到排序,之后按顺序插入线段树;

ans累加的是最终结果,num[1]是整个区间内连续的线段数(将竖边投影到y轴的区间),len[1]表示整个区间内被覆盖的长度,而last保存的是上一次的len[1];

也就是说,从左往右在每一次插入一条边后,周长并的累加值==新增的横边+新增的竖边。

我们可以发现,插入一条边之后,新增的横边的树木等于区间内连续线段的数目*新增横边的长度,新增的竖边等于插入前后覆盖长度的差值。

结合下面的图片我们会发现:插入一条出边之后,其实等同于删除该矩形对应的入边。对应下图,此时也就出现了不连续的线段了。横边也相应增加了。

注意:不需要重复建树,因为插入所有边后,相对的也将树中可能产生影响的域给清空了。

 

结合代码吧:

 

#include<iostream>
#include<algorithm>
#define maxn 20010
using namespace std;
struct node
{
	int x,y1,y2,s;
	node(int a=0,int b=0,int c=0,int d=0):x(a) , y1(b) , y2(c) , s(d) {}
	friend bool operator <(const node a,const node b)
	{
		return a.x<b.x;
	}
};
node ss[maxn>>1];//保存矩形的边,其中s表示该边为入边还是出边
bool cmp(node a,node b)
{
	return a.x<b.x;
}
bool lb[maxn<<2],rb[maxn<<2];
int cnt[maxn<<2],len[maxn<<2],num[maxn<<2];
//len[]表示该区间被覆盖的总长度
//num[]表示该区间内连续的线段数目
//cnt[]表示该区间被覆盖的次数
void PushUp(int k,int s,int t)
{
	if(cnt[k])
	{
		lb[k]=rb[k]=1;
		num[k]=2;
		len[k]=t-s+1;
	}
	else if(s==t)
		lb[k]=rb[k]=num[k]=len[k]=0;
	else {
		num[k]=num[k<<1]+num[k<<1 |1];
		len[k]=len[k<<1]+len[k<<1 |1];
		lb[k]=lb[k<<1];
		rb[k]=rb[k<<1|1];
		if(rb[k<<1] && lb[k<<1 |1])//左右子区间的有一个线段相连
			num[k]-=2;
	}
}
void update(int l,int r,int c,int s,int t,int k)
{
	if(l<=s && t<=r)
	{
		cnt[k]+=c;//插边或删边
		PushUp(k,s,t);
		return ;
	}
	int kl=k<<1,kr=kl+1,mid=(s+t)>>1;
	if(l<=mid)
		update(l,r,c,s,mid,kl);
	if(r>mid)
		update(l,r,c,mid+1,t,kr);
	PushUp(k,s,t);
}
int main()
{
	int a,b,c,d,n;
	int miny,maxy;
	while(scanf("%d",&n)==1)
	{
		int m=0;
		miny=10000;
		maxy=-10000;
		for(int i=0;i<n;i++)
		{
			scanf("%d %d %d %d",&a,&b,&c,&d);
			miny=min(miny,b);
			maxy=max(maxy,d);
			ss[m++]=node(a,b,d,1);
			ss[m++]=node(c,b,d,-1);
		}
		sort(ss,ss+m);
		int ans=0,last=0;
		for(int i=0;i<m;i++)
		{
			if(ss[i].y1<ss[i].y2)
			update(ss[i].y1,ss[i].y2-1,ss[i].s,miny,maxy-1,1);
			ans+=num[1]*(ss[i+1].x-ss[i].x);
			ans+=abs(len[1]-last);
			last=len[1];
		}
		printf("%d\n",ans);
	}
	return 0;
}

转载于:https://www.cnblogs.com/nanke/archive/2011/10/02/2198044.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值