做这题建议看一下该题的discuss。
#include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> #include <set> /* 题意:给出n张海报的左端点和右端点,按顺序往墙上贴。问最后整张墙能看到几张海报。 注意:按题意是如此够造树的,即每个点其实是一小块段。 |___|___|___|___|___|___|___|___| 1 2 3 4 5 6 7 8 思路:相当于染色问题,这里我每个节点有个color值。 color值有三种情况:1.0表示该区间为染色;2.-1表示该区间含有多种颜色;3.正整数表示该区间全是一种颜色 每次更新(即第i次“涂色”)的时候,如果区间在范围内,则该区间color值替换为此次的颜色(用i表示)。 最后查询的时候,从根节点往下查,直到查询到color值为某一个正整数,标记该颜色对应的序号。 最后统计有多少个标记,即为答案。 由于数据大,要离散化,这才是重点!!! 有些人离散化,直接按顺序相邻,即每次都只加1。这是错误的!!! 给个数据就明白了: 1 3 1 10 1 3 6 10 正确答案:3 错误答案:2 忽略了顺序相邻但位置不相邻的情况 如果直接按顺序离散: 1 4 1 2 3 4 那么1 2和3 4的就会覆盖掉1 4,得出错误结果2 即离散的时候3和6之间应该还要留出一段,即总共离散为5段,不然会把中间夹的给覆盖掉了 所以离散的时候,排好序后,如果两个相邻的仅相差1,那么对应的映射+1即可。 但如果相差大于1,则对应的映射+2,留出个空隙。 在discuss里会有人说,如果按这正确的方法做,提交会WA;全部改成+1,才AC。 其实+2导致WA的原因,是数组开的不够。 我原本开的是tree[maxn<<3]的大小,用正确做法做,WA;但开大后,tree[maxn<<4],就AC了。 也就是说,POJ上其实没有这类数据 */ using namespace std; const int maxn=10005; int n,m,idx; int maxh; //对应映射的最大值 int hash_val[10000005]; //对应的映射 int val[maxn*2]; //存储端点的值 int vis[maxn]; //用来标记颜色 //存储海报的左端点、右端点 struct Pos{ int left,right; }pos[maxn]; struct Node{ int color; //0:该区间没有染色;-1:该区间含多种颜色;正整数:该区间涂满一种颜色。 bool lazy; }tree[maxn<<4]; //10000个海报,每个海报两个端点,所以最多会有20000个点,但开4倍不够,要开8倍,这样+2的方法才能AC void build(int rt,int L,int R){ tree[rt].color=0; tree[rt].lazy=false; if(L==R) return; int mid=(L+R)>>1; build(rt<<1,L,mid); build(rt<<1|1,mid+1,R); } void pushUp(int rt){ int lson=rt<<1,rson=rt<<1|1; //只要有一个子区间上面含多种颜色(color值为-1),该节点color值为-1 if(tree[lson].color==-1 || tree[rson].color==-1) tree[rt].color=-1; //两个子区间都没涂有颜色 else if(!tree[lson].color && !tree[rson].color) tree[rt].color=0; //两个子区间各自都涂有一种颜色 else if(tree[lson].color && tree[rson].color){ if(tree[lson].color==tree[rson].color) tree[rt].color=tree[lson].color; else tree[rt].color=-1; } else{ //一个为0(即该区间没有染色),另一个为正数 tree[rt].color=tree[lson].color|tree[rson].color; } } void pushDown(int rt){ if(tree[rt].lazy){ int lson=rt<<1,rson=rt<<1|1; tree[lson].color=tree[rson].color=tree[rt].color; tree[lson].lazy=tree[rson].lazy=true; tree[rt].lazy=false; } } void update(int rt,int l,int r,int L,int R,int color){ if(l<=L && R<=r){ tree[rt].lazy=true; tree[rt].color=color; return; } pushDown(rt); int mid=(L+R)>>1; if(r<=mid) update(rt<<1,l,r,L,mid,color); else if(l>mid) update(rt<<1|1,l,r,mid+1,R,color); else{ update(rt<<1,l,mid,L,mid,color); update(rt<<1|1,mid+1,r,mid+1,R,color); } pushUp(rt); } void query(int rt,int L,int R){ //下面两种情况已经包含了叶子节点,因为叶子节点的color只能为0或者>0,不可能为-1。所以不会RE if(tree[rt].color>0){ //如果该区间涂有一种颜色,则标记该颜色 vis[tree[rt].color]=1; return; } //如果区间没有染色,直接return。 if(!tree[rt].color){ return; } int mid=(L+R)>>1; query(rt<<1,L,mid); query(rt<<1|1,mid+1,R); } int main() { int t; scanf("%d",&t); while(t--){ scanf("%d",&n); idx=0; for(int i=1;i<=n;i++){ scanf("%d%d",&pos[i].left,&pos[i].right); val[idx++]=pos[i].left; val[idx++]=pos[i].right; } sort(val,val+idx); hash_val[val[0]]=1; //离散 for(int i=1;i<idx;i++){ if(val[i]==val[i-1]) hash_val[val[i]]=hash_val[val[i-1]]; //如果两个端点不相邻,则+2 else if(val[i]-val[i-1]>1) hash_val[val[i]]=hash_val[val[i-1]]+2; //如果两个端点相邻,则+1. else{ hash_val[val[i]]=hash_val[val[i-1]]+1; } maxh=hash_val[val[i]]; } build(1,1,maxh); for(int i=1;i<=n;i++){ update(1,hash_val[pos[i].left],hash_val[pos[i].right],1,maxh,i); } memset(vis,0,sizeof(vis)); query(1,1,maxh); int ans=0; for(int i=1;i<=n;i++){ if(vis[i]){ ans++; } } printf("%d ",ans); } return 0; }