线段树扫描线。
题意: 在一个n*m平面内,有一些不重叠的矩形(可能退化成点或线),现在让你把一根给定长度的线竖直或水平地放进平面,要求不与任何原来存在的矩形重合,问有多少种放法。
解法: 在一根长度为a的线段上放置一根长度为b的线段共有a-b+1种放法。那么将给定的矩形拆成两根线段,用扫描线横着扫一次,竖着扫一次,就可以得到答案。有区间合并的过程。范围较大需要离散化。
本人在拆矩形时因为拆的是闭区间,一个矩形8条线,再因为线段树的4倍空间需求和维护变量的4倍空间需求,总共有5W个矩形,在ZOJ上内存堪堪卡过去。
注意一下计算时的讨论,还有长度为1的情况,见代码,不赘述。
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define REP(i,a,b) for(int i=(a); i<(b); i++)
#define clr(a,b) memset(a,b,sizeof(a))
typedef long long lld;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define ls (rt<<1)
#define rs (rt<<1|1)
const int MAXN = 400010;
int w,h,n,len;
int san[MAXN],st;
struct point {
int x[2],y[2];
}sp[MAXN];
struct Seg {
int l,r,h,s;
Seg(){}
Seg(int l, int r, int h, int s) :
l(l), r(r), h(h), s(s) {}
bool operator < (const Seg &tt) const {
if(h == tt.h) return s > tt.s;
return h < tt.h;
}
}ss[MAXN<<2];
int tot;
lld ans;
int lpa[MAXN<<2],rpa[MAXN<<2],sum[MAXN<<2],cnt[MAXN<<2];
int Count(int x) {
return x < len ? 0 : x-len+1;
}
void up(int l, int r, int rt) {
int m = l+r>>1;
sum[rt] = sum[ls] + sum[rs];
lpa[rt] = lpa[ls];
if(lpa[rt] == m-l+1) lpa[rt] += lpa[rs];
rpa[rt] = rpa[rs];
if(rpa[rt] == r-m) rpa[rt] += rpa[ls];
sum[rt] -= Count(san[m]-san[m-rpa[ls]+1]+1);
sum[rt] -= Count(san[m+lpa[rs]]-san[m+1]+1);
sum[rt] += Count(san[m+lpa[rs]]-san[m-rpa[ls]+1]+1);
}
void build(int l, int r, int rt) {
sum[rt] = Count(san[r]-san[l]+1);
lpa[rt] = rpa[rt] = r-l+1;
cnt[rt] = 0;
if(l == r) return ;
int m = l+r>>1;
build(lson);
build(rson);
}
void update(int L, int R, int x, int l, int r, int rt) {
if(L<=l && r<=R) {
cnt[rt] += x;
if(cnt[rt] == 0) {
lpa[rt] = rpa[rt] = r - l + 1;
sum[rt] = Count(san[r]-san[l]+1);
}
else {
rpa[rt] = lpa[rt] = 0;
sum[rt] = 0;
}
return ;
}
else {
int m = l+r>>1;
if(L<=m) update(L,R,x,lson);
if(m< R) update(L,R,x,rson);
up(l,r,rt);
}
}
void gaox() {
if(n == 0) {
ans += (lld)Count(w) * h;
return ;
}
if(w < len) return ;
tot = 0;
REP(i,0,n) {
ss[tot++] = Seg(sp[i].x[0],sp[i].x[1],sp[i].y[0],1);
ss[tot++] = Seg(sp[i].x[0],sp[i].x[1],sp[i].y[1],-1);
}
sort(ss,ss+tot);
int l = lower_bound(san,san+st,1) - san;
int r = lower_bound(san,san+st,w) - san;
build(l,r,1);
REP(i,0,tot-1) {
int L = lower_bound(san,san+st,ss[i].l) - san;
int R = lower_bound(san,san+st,ss[i].r) - san;
if(ss[i].s < 0 && (ss[i-1].h != ss[i].h || ss[i-1].s != ss[i].s))
ans += sum[1];
update(L,R,ss[i].s,l,r,1);
if(ss[i].s < 0 && ss[i+1].h != ss[i].h) ans += (lld)sum[1] * (ss[i+1].h - ss[i].h - 1);
else if(ss[i].s > 0) ans += (lld)sum[1] * (ss[i+1].h - ss[i].h);
}
if(ss[tot-1].s != ss[tot-2].s || ss[tot-1].h != ss[tot-2].h) ans += sum[1];
ans += (lld)Count(w) * (ss[0].h-1 + h-ss[tot-1].h);
}
void gaoy() {
if(n == 0) {
ans += (lld)Count(h) * w;
return ;
}
if(h < len) return ;
tot = 0;
REP(i,0,n) {
ss[tot++] = Seg(sp[i].y[0],sp[i].y[1],sp[i].x[0],1);
ss[tot++] = Seg(sp[i].y[0],sp[i].y[1],sp[i].x[1],-1);
}
sort(ss,ss+tot);
int l = lower_bound(san,san+st,1) - san;
int r = lower_bound(san,san+st,h) - san;
build(l,r,1);
REP(i,0,tot-1) {
int L = lower_bound(san,san+st,ss[i].l) - san;
int R = lower_bound(san,san+st,ss[i].r) - san;
int temp;
if(ss[i].s < 0 && (ss[i-1].h != ss[i].h || ss[i-1].s != ss[i].s)) ans += sum[1];
update(L,R,ss[i].s,l,r,1);
if(ss[i].s < 0 && ss[i+1].h != ss[i].h) ans += (lld)sum[1] * (ss[i+1].h - ss[i].h - 1);
else if(ss[i].s > 0) ans += (lld)sum[1] * (ss[i+1].h - ss[i].h);
}
if(ss[tot-1].s != ss[tot-2].s || ss[tot-1].h != ss[tot-2].h) ans += sum[1];
ans += (lld)Count(h) * (ss[0].h-1 + w-ss[tot-1].h);
}
int main() {
while(~scanf("%d%d%d%d", &w, &h, &n, &len)) {
st = 0;
ans = 0;
san[st++] = 1;
san[st++] = w;
san[st++] = h;
REP(i,0,n) {
REP(j,0,2) {
scanf("%d%d", &sp[i].x[j],&sp[i].y[j]);
san[st++] = sp[i].x[j];
san[st++] = sp[i].y[j];
}
san[st++] = sp[i].x[0]-1;
san[st++] = sp[i].x[1]+1;
san[st++] = sp[i].y[0]-1;
san[st++] = sp[i].y[1]+1;
}
sort(san,san+st);
st = unique(san,san+st) - san;
gaox();
if(len != 1) gaoy();
printf("%lld\n", ans);
}
return 0;
}
/*
4 4 3 2
3 2 3 2
2 3 2 3
3 3 4 4
ans = 10
*/