FZU2187 – 回家种地(扫描线+离散)

题目链接:FZU2187 【分析】就是扫描线的裸体,本来不会扫描线,特地去学了下。具体看注释。 【AC CODE】890ms

#include <cstdio>
#include <cstring>
#include <cctype>
#include <cmath>
#include <set>
//#include <unordered_set>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
typedef long long LL;
#define rep(i,a,n) for(int i = a; i < n; i++)
#define repe(i,a,n) for(int i = a; i <= n; i++)
#define per(i,n,a) for(int i = n; i >= a; i--)
#define clc(a,b) memset(a,b,sizeof(a))
/*扫描线+线段树辅助
把矩形的上下边存起来,并且标记为-1和1;
从下往上每次扫描到一条边的时候计算横坐标覆盖的x长度(用线段树维护)*当前边和下一条边的高度差
就是不重复分一小块面积,全部扫描完就是所有矩形面积的并
线段树记录的是点x~y+1的并,而线段少了个端点,所以加入删除线段x,y的话对应线段树操作x~y-1
*/
typedef LL type;
const int INF = 0x3f3f3f3f, MAXN = 200000+10;
struct NODE{//扫描线段
	type l,r,h;
	int v;
	bool operator<(const NODE &t) const{
		return h < t.h;
	}
}p[MAXN];
int tol;//线段数量
type pos[MAXN], tmp[MAXN];//离散x坐标,二分找pos
int cnt;//x坐标离散后的数量

int cov[MAXN<<1], lz[MAXN<<1];//cov记录该段完全覆盖次数,lz是cov的lazy标记(可以不用)
type sum2[MAXN<<1], sum1[MAXN<<1], one[MAXN<<1];
//sum2记录被覆盖2次及以上的区间长度,sum1是一次以上的,one只覆盖一次的长度
void init()
{
	clc(sum2,0);
	clc(sum1,0);
	clc(cov,0);
	clc(lz,0);
}
inline int id(int x, int y){return x+y|x!=y;}
void push_up(int x, int y, int m)
{
	/*计算覆盖一次以及以上的长度*/
	int u = id(x,y), l = id(x,m), r = id(m+1,y);
	if(cov[u]) sum1[u] = pos[y+1]-pos[x];//完全覆盖
	else if(x == y) sum1[u] = 0;//叶子结点
	else sum1[u] = sum1[l]+sum1[r];
	/*计算覆盖2次以及2次以上的长度*/
	if(2 <= cov[u]) sum2[u] = pos[y+1]-pos[x],sum1[u];
	else if(x == y) sum2[u] = 0;
	else if(1 == cov[u]) sum2[u] = sum1[l]+sum1[r];//当前覆盖了1次只要加上覆盖1次的儿子就是覆盖2次的值
	else sum2[u] = sum2[l]+sum2[r];
	/*减一下就是只覆盖一次的长度了*/
	one[u] = sum1[u]-sum2[u];
}
/*这个向下传递可以不用*/
void push_down(int x, int y, int m)
{
	int u = id(x,y);
	if(lz[u])
	{
		int l = id(x,m), r = id(m+1,y);
		cov[l] += lz[u], cov[r] += lz[u];
		lz[l] += lz[u], lz[r] += lz[u];
		lz[u] = 0;
	}
}
void update(int x, int y, int ql, int qr, int v)
{
	int m = (x+y)>>1;
	if(ql <= x && y <= qr)
	{
		cov[id(x,y)] += v;
		push_up(x,y,m);
		return;
	}
	push_down(x,y,m);
	if(ql <= m) update(x,m,ql,qr,v);
	if(qr > m) update(m+1,y,ql,qr,v);
	push_up(x,y,m);
}
int main()
{
#ifdef SHY
	freopen("e:\\1.txt", "r", stdin);
#endif
	int t,count = 0;
	scanf("%d%*c", &t);
	while(t--)
	{
		int n;
		scanf("%d%*c", &n);
		cnt = tol = 0;
		int nn = 0;
		rep(i,0,n)
		{
			type x1,y1,x2,y2;
			scanf("%I64d %I64d %I64d %I64d%*c", &x1, &y1, &x2, &y2);
			tmp[nn++] = x1, tmp[nn++] = x2;
			p[tol].h = y1, p[tol].l = x1, p[tol].r = x2, p[tol++].v = 1;
			p[tol].h = y2, p[tol].l = x1, p[tol].r = x2, p[tol++].v = -1;
		}
		sort(p,p+tol);
		sort(tmp,tmp+nn);
		pos[cnt++] = tmp[0];
		rep(i,1,nn)//tmp去重
		{
			if(tmp[i] != tmp[i-1]) pos[cnt++] = tmp[i];
		}
		type ans = 0;
		init();
		rep(i,0,tol)
		{
			int x = lower_bound(pos,pos+cnt,p[i].l)-pos, y = lower_bound(pos,pos+cnt,p[i].r)-pos-1;
			update(0,cnt-1,x,y,p[i].v);
			ans += one[id(0,cnt-1)]*(p[i+1].h-p[i].h);
		}
		printf("Case %d: %I64d\n",++count, ans);
	}
	return 0;
}

 

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

请输入正确的验证码