Usaco Training Section 5.5 Picture

该博客探讨了如何利用扫描线算法解决多个矩形周长之和的问题,与之前5.3题目的Window Area问题类似。博主提到了线段树的解决方案,并提供了链接以供深入学习。此外,博主分享了一种暴力排序的方法,并指出此问题在HDU平台上存在陷阱,需要注意处理多组数据的情况,否则可能导致错误答案。

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

求许多矩形的周长并。可以联想到5.3的Window Area,这道题是求面积并,我们可以用扫描线+排序推一推/线段树。

这道题我们同样可以用扫描线来解决。

线段树稍微麻烦一些,具体请见https://blog.youkuaiyun.com/tomorrowtodie/article/details/52048323

我的代码:

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define inf 2147483647
#define mp make_pair
#define pii pair<int,int>
#define pb push_back
#define r1 rt<<1
#define r2 rt<<1|1
#define ld long double
using namespace std;

inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x*f;
}

const int N=10005,M=30000;
struct node{
	int x1,x2,y,k;
	bool operator < (const node &b) const{
		if(y!=b.y) return y<b.y;
		else return k>b.k;
	}
}a[N];

struct seg{
	int l,r,len,num,sum,lc,rc;
}st[M<<2];

inline void pushup(int rt){
	if(st[rt].sum>0){
		st[rt].len=st[rt].r-st[rt].l+1;
		st[rt].lc=st[rt].rc=1;
		st[rt].num=1;
	}
	else if(st[rt].l==st[rt].r){
		st[rt].len=st[rt].num=st[rt].lc=st[rt].rc=0;
	}
	else{
		st[rt].len=st[r1].len+st[r2].len;
		st[rt].lc=st[r1].lc;st[rt].rc=st[r2].rc;
		st[rt].num=st[r1].num+st[r2].num-(st[r1].rc&st[r2].lc);
	}
}

inline void build(int l,int r,int rt){
	st[rt].l=l;st[rt].r=r;st[rt].len=st[rt].num=st[rt].sum=st[rt].lc=st[rt].rc=0;
	if(l==r) return;
	int m=(l+r)>>1;
	build(l,m,r1);
	build(m+1,r,r2);
}

inline void update(int l,int r,int c,int rt){
	if(st[rt].l>r||l>st[rt].r) return;
	if(l<=st[rt].l&&st[rt].r<=r){
		st[rt].sum+=c;
		pushup(rt);
		return;
	}
	update(l,r,c,r1);
	update(l,r,c,r2);
	pushup(rt);
}

int main()
{
	freopen("picture.in","r",stdin);
	freopen("picture.out","w",stdout);
	int n=read(),m=0,mx=-inf,mn=inf;
	for(int i=1;i<=n;++i){
		int x1=read(),y1=read(),x2=read(),y2=read();
		mx=max(mx,max(x1,x2));
        mn=min(mn,min(x1,x2));
		a[++m].x1=x1,a[m].x2=x2,a[m].y=y1,a[m].k=1;
		a[++m].x1=x1,a[m].x2=x2,a[m].y=y2,a[m].k=-1;
	}
	sort(a+1,a+m+1);
	build(mn,mx-1,1);
	int ans=0,last=0;
	for(int i=1;i<=m;++i){
		update(a[i].x1,a[i].x2-1,a[i].k,1);
		//横线
		ans+=abs(st[1].len-last);
		//竖线 
		ans+=(a[i+1].y-a[i].y)*2*st[1].num;
		last=st[1].len;
	}
	printf("%d\n",ans);
	return 0;
}

其实这题可以暴力,排个序推一推。

将横边和纵边分开算,具体直接看代码就行了,很好理解。

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define inf 2147483647
#define mp make_pair
#define pii pair<int,int>
#define pb push_back
#define r1 rt<<1
#define r2 rt<<1|1
#define ld long double
using namespace std;

inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x*f;
}

struct node{
	int x1,x2,y,k;
	bool operator < (const node &b) const{
		if(y!=b.y) return y<b.y;
		else return k>b.k;
	}
}a[20005];
struct nod{
	int x1,x2,y1,y2;
}b[20005];
int ans,n,m,f[2][20005];

inline void work(){
	sort(a+1,a+m+1);
	memset(f,0,sizeof(f));
	int x=1;
	while(x<=m){
		int y=a[x].y;
		for(int i=1;i<=20000;++i) f[0][i]=f[1][i];
		while(x<=m&&a[x].y==y){
			for(int i=a[x].x1+10000;i<a[x].x2+10000;++i) f[1][i]+=a[x].k;
			++x;
		}
		for(int i=1;i<=20000;++i){
			if(f[0][i]==0&&f[1][i]>0) ++ans;
			if(f[1][i]==0&&f[0][i]>0) ++ans;
		}
	}
}

int main()
{
	freopen("picture.in","r",stdin);
	freopen("picture.out","w",stdout);
	n=read();m=0;
	for(int i=1;i<=n;++i){
		int x1=read(),y1=read(),x2=read(),y2=read();
		a[++m].x1=x1,a[m].x2=x2,a[m].y=y1,a[m].k=1;
		a[++m].x1=x1,a[m].x2=x2,a[m].y=y2,a[m].k=-1;
		b[i].x1=x1,b[i].y1=y1,b[i].x2=x2,b[i].y2=y2;
	}
	work();
	m=0;
	for(int i=1;i<=n;++i){
		a[++m].x1=b[i].y1,a[m].x2=b[i].y2,a[m].y=b[i].x1,a[m].k=1;
		a[++m].x1=b[i].y1,a[m].x2=b[i].y2,a[m].y=b[i].x2,a[m].k=-1;
	}
	work();
	printf("%d\n",ans);
	return 0;
}

吐槽一下:这题在HDU上也有,但有个坑人的地方,在HDU上这题有多组数据,要修改一下。因为这个wa了好几次啊!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值