题意:给定一些海报,可能互相重叠,告诉你每个海报的起始和终止坐标(高度都一样)和先后叠放次序,问没有被完全盖住(露出)的海报有多少张。
思路(部分内容摘自poj课件):线段树+离散化。节点增添flag域,用来表示该段是否已经被覆盖。
如果每个叶子节点都代表一块瓷砖,那么线段树会导致MLE。实际上,由于最多10,000个海报,共计20,000个端点,这些端点把墙最多分成19,999个单位区间,我们只要对这19,999个区间编号,然后建树即可。这就是离散化。
更好的离散化方法,是将所有海报的端点瓷砖排序,把每个海报的端点瓷砖都看做一个单位区间,两个相邻的端点瓷砖之间的部分是一个单位区间。这样最多会有20000 + 19999个单位区间。
另外注意,插入的顺序为从上至下,这样后插入的海报不可能覆盖先插入的海报,因此插入一张海报时,如果发现海报对应区间有一部分露出来,就说明该海报部分可见。
用数组开线段树比较奇葩,G++AC,C++则WA。原因不明啊,老师说可能是数组越界,但是我通过进行二分查找而避免开那个10000000的hash数组来求区间(见版本3),仍然是g++AC,c++WA....
poj3277的意思是给定每个矩形的高度以及底边在数轴上的起点和终点。各矩形间可能有重叠。问它们覆盖的总面积是多少。思路同2528。需要注意的是初始化要按照h从小到大排序。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 10100
typedef struct node{
int flag;
int left,right;
struct node *lson,*rson;
}tree;
int T,n,top,len,res;
int poster[N][2];//保存海报端点,逆序更新
int s[N*2];//海报的端点瓷砖编号,用于排序
int hash[10000010];//hash[i]表示瓷砖端点为i所处的离散化后的区间编号
tree t[N*100];
tree *root,*alloc;
int cmp(const int *a,const int *b){
return (*a)-(*b);
}
int getmid(tree *r){
return (r->left+r->right)/2;
}
void create(tree *r,int L,int R){
int mid;
r->left = L;
r->right = R;
r->flag = 1;
if(L != R){
mid = getmid(r);
r->lson = ++alloc;
create(r->lson,L,mid);
r->rson = ++alloc;
create(r->rson,mid+1,R);
}
}
int update(tree *r,int L,int R){
int mid = getmid(r);
int temp;
if(r->flag == 0)
return 0;
if(r->left==L && r->right==R){
r->flag = 0;
return 1;
}
else if(R <= mid)
temp = update(r->lson,L,R);
else if(L > mid)
temp = update(r->rson,L,R);
else{
int temp1,temp2;
temp1 = update(r->lson,L,mid);
temp2 = update(r->rson,mid+1,R);
temp = temp1 | temp2;<span style="color:#ff6666;">//必须用两个临时变量先表示出来再取或,如果直接相或,那么前面的成立后面的将不会执行。</span>
}
if(!r->lson->flag && !r->rson->flag)<span style="color:#ff6666;">//要更新根节点的覆盖情况,不更新会出错</span>
r->flag = 0;
return temp;
}
int main(){
freopen("a.txt","r",stdin);
scanf("%d",&T);
while(T--){
int i,j;
top = res = 0 ;
root = alloc = t;
scanf("%d",&n);
for(i = 0;i<n;i++){
scanf("%d %d",&poster[i][0],&poster[i][1]);
s[i*2] = poster[i][0];
s[i*2+1] = poster[i][1];
}
qsort(s,2*n,sizeof(int),cmp);
for(i=0,j=1;j<n*2;j++)//去掉重复元素
if(s[i] != s[j])
s[++i] = s[j];
len = i;
for(i = 0;i<=len;i++){
hash[s[i]] = ++top;
if(i<len && s[i+1]-s[i]>1)
top++;
}
create(root,1,top);
for(i = n-1;i>=0;i--)
res += update(root,hash[poster[i][0]],hash[poster[i][1]]);
printf("%d\n",res);
}
return 0;
}
数组版本:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define N 10005
#define INF 0x3fffffff
using namespace std;
int hh[10000005];
struct tree{
int l,r;
bool flag;
}t[N*4];
int p[N][2],s[N<<1];
int n,T,len,m;
int mid(int a,int b){
return (a+b)>>1;
}
void createTree(int root,int a,int b){
t[root].l = a;
t[root].r = b;
t[root].flag = false;
if(a != b){
createTree(root*2, a, mid(a, b));
createTree(root*2+1, mid(a,b)+1, b);
}
}
bool insert(int root,int a,int b){
bool res;
int mm = mid(t[root].l,t[root].r);
if(t[root].flag)
return false;
if(t[root].l == a && t[root].r == b){
t[root].flag = true;
return true;
}
if(b <= mm)
res = insert(root*2, a, b);
else if(a>mm)
res = insert(root*2+1, a, b);
else{
bool tmp1,tmp2;
tmp1 = insert(root*2, a, mm);
tmp2 = insert(root*2+1, mm+1, b);
res = tmp1 || tmp2;
}
if(t[root*2].flag && t[root*2+1].flag)
t[root].flag = true;
return res;
}
int main(){
scanf("%d",&T);
while(T--){
int i,j,res=0;
len = m = 0;
scanf("%d",&n);
for(i = 1;i<=n;i++){
scanf("%d %d",&p[i][0],&p[i][1]);
s[len++] = p[i][0];
s[len++] = p[i][1];
}
sort(s,s+len);
for(i = j = 0;i<len;i++){
if(i && s[i] == s[i-1])
continue;
s[j++] = s[i];
}
len = j;
for(i = 0;i<len;i++){
hh[s[i]] = ++m;
if(i<len-1 && s[i+1]-s[i]>1)//必不可少,想想如下例子:三个海报,顺序:(6,8)(4,6)(8,10)
++m;
}
createTree(1,1,m);
for(i = n;i>=1;i--){
if(insert(1,hh[p[i][0]],hh[p[i][1]]))
res++;
}
printf("%d\n",res);
}
return 0;
}
版本3:数组建线段树,采用二分查找找到区间,不开一千万的hash数组:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define N 10005
#define INF 0x3fffffff
using namespace std;
struct tree{
int l,r;
bool flag;
}t[N<<4];
int p[N][2],s[N<<1],hh[N<<2];
int n,T,len,m;
int mid(int a,int b){
return (a+b)>>1;
}
void createTree(int root,int a,int b){
t[root].l = a;
t[root].r = b;
t[root].flag = false;
if(a != b){
createTree(root*2, a, mid(a, b));
createTree(root*2+1, mid(a,b)+1, b);
}
}
bool insert(int root,int a,int b){
bool res;
int mm = mid(t[root].l,t[root].r);
if(t[root].flag)
return false;
if(t[root].l == a && t[root].r == b){
t[root].flag = true;
return true;
}
if(b <= mm)
res = insert(root*2, a, b);
else if(a>mm)
res = insert(root*2+1, a, b);
else{
bool tmp1,tmp2;
tmp1 = insert(root*2, a, mm);
tmp2 = insert(root*2+1, mm+1, b);
res = tmp1 || tmp2;
}
if(t[root*2].flag && t[root*2+1].flag)
t[root].flag = true;
return res;
}
int find(int x){
int low = 1,high = m,mid;
while(low <= high){
mid = (low+high)>>1;
if(hh[mid] == x)
return mid;
else if(hh[mid] <= x)
low = mid+1;
else
high = mid-1;
}
return 0;
}
int main(){
scanf("%d",&T);
while(T--){
int i,j,a,b,res=0;
len = m = 0;
scanf("%d",&n);
for(i = 1;i<=n;i++){
scanf("%d %d",&p[i][0],&p[i][1]);
s[len++] = p[i][0];
s[len++] = p[i][1];
}
sort(s,s+len);
for(i = j = 0;i<len;i++){
if(i && s[i] == s[i-1])
continue;
s[j++] = s[i];
}
len = j;
for(i = 0,m=0;i<len;i++){
if(i && s[i]-s[i-1]>1)//必不可少,想想如下例子:三个海报,顺序:(6,8)(4,6)(8,10)
hh[++m] = s[i]-1;
hh[++m] = s[i];
}
createTree(1,1,m);
for(i = n;i>=1;i--){
a = find(p[i][0]);
b = find(p[i][1]);
if(insert(1, a, b))
res++;
}
printf("%d\n",res);
}
return 0;
}
3277:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstdlib>
using namespace std;
#define clc(s,t) memset(s,t,sizeof(s))
#define INF 0x3fffffff
#define N 70005
struct node{
int a,b,h;
}p[N];
struct tree{
int l,r,len,h;
}t[N<<3];
int s[N<<1],n,m;
int mid(int x,int y){
return (x+y)>>1;
}
void create(int root,int l,int r){
t[root].l = l;
t[root].r = r;
t[root].len = s[r]-s[l-1];
t[root].h = 0;
if(l==r)
return;
create(root*2, l, mid(l,r));
create(root*2+1, mid(l,r)+1, r);
}
void update(int r,int a,int b,int h){
int mm = mid(t[r].l,t[r].r);
if(t[r].l == a && t[r].r == b){
t[r].h = h;
return;
}
if(t[r].h){
t[r*2].h = t[r*2+1].h = t[r].h;
t[r].h = 0;
}
if(b<=mm)
update(r*2, a, b, h);
else if(a>mm)
update(r*2+1, a, b, h);
else{
update(r*2, a, mm, h);
update(r*2+1, mm+1, b, h);
}
}
long long area(int r){
long long res = 0;
if(t[r].h)
return (long long)t[r].len * t[r].h;
if(t[r].l == t[r].r)
return 0;
res += area(r*2);
res += area(r*2+1);
return res;
}
int cmp(node a,node b){
return a.h<b.h;
}
int main(){
int i,j;
scanf("%d",&n);
for(i = 0;i<n;i++){
scanf("%d %d %d",&p[i].a,&p[i].b,&p[i].h);
s[i*2] = p[i].a;
s[i*2+1] = p[i].b;
}
sort(p,p+n,cmp);
sort(s,s+2*n);
m = unique(s, s+2*n) - s;
create(1,1,m-1);
for(i = 0;i<n;i++){
int a = lower_bound(s, s+m, p[i].a)-s+1;
int b = lower_bound(s, s+m, p[i].b)-s;
update(1,a,b,p[i].h);
}
printf("%lld\n",area(1));
return 0;
}