poj 2528/3277 线段树(海报覆盖)

题意:给定一些海报,可能互相重叠,告诉你每个海报的起始和终止坐标(高度都一样)和先后叠放次序,问没有被完全盖住(露出)的海报有多少张。

思路(部分内容摘自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;
}


你可能感兴趣的:(poj 2528/3277 线段树(海报覆盖))