题意:给定一些海报,可能互相重叠,告诉你每个海报的起始和终止坐标(高度都一样)和先后叠放次序,问没有被完全盖住(露出)的海报有多少张。
思路(部分内容摘自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; }