题意:
输入t组数据,输入n代表有n块广告牌,按照顺序贴上去,输入区间,问贴完以后还有多少块广告牌可以看到(因为有的被完全覆盖了)。
输入:2 4
表示这块广告牌占了第2、3、4个格子。
思路:
这是占格子类型的题,Count the Colors是涂颜色的问题两者有点区别。可以理解为这题是涂[l,r]上的点,而Count the Colors是涂[l,r]上的线段
输入的区间太大,不能直接用线段树求,需要先离散化。
将左右端点离散化,先用数组存每一个端点,排序然后去重,每个端点离散化后的值就是它在数组中的下标。考虑到一个区间两个端的格子被覆盖对它的影响,如果这个区间格子为那么它就被完全覆盖了,如果这个区间长度大于那么它中间的部分还能看到。所以在离散化时,如果,需要加上端点,使得这个区间在离散化后的区间内格子的数量大于2,从而不破坏这个区间的特征。
的作用是“去掉”容器中相邻元素的重复元素(不一定要求数组有序),它会把重复的元素添加到容器末尾(所以数组大小并没有改变),而返回值是去重之后的尾地址,这个尾地址是从前面移过来的第一个重复元素的下标。
只将对应区间涂上对应的颜色(赋值为i),子区间不管,父区间不涂色。统计时,标记某种颜色是否统计过,一旦发现一个区间涂了色处理完就退出,不必访问子区间。
Code:
#include<algorithm> #include<cstring> #include<cstdio> using namespace std; typedef long long ll; const int maxn=1e4+7; inline ll read() { ll s = 0, w = 1; char ch = getchar(); while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); } while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar(); return s * w; } int tree[maxn<<4],que[maxn<<2],li[maxn],ri[maxn],ans,tot; bool vis[maxn]; inline void pushdown(int rt) { tree[rt<<1]=tree[rt<<1|1]=tree[rt]; tree[rt]=-1; } inline void update(int x,int y,int val,int l,int r,int rt) { if(x<=l&&y>=r) { tree[rt]=val; return; } int mid=l+r>>1; if(~tree[rt]) pushdown(rt); if(x<=mid) update(x,y,val,l,mid,rt<<1); if(y>mid) update(x,y,val,mid+1,r,rt<<1|1); } inline void query(int l,int r,int rt) { if(~tree[rt]&&!vis[tree[rt]]) { ans+=1; vis[tree[rt]]=true; return; } if(l==r) return; int mid=l+r>>1; if(~tree[rt]) pushdown(rt); query(l,mid,rt<<1); query(mid+1,r,rt<<1|1); } int main() { int t=read(),n,mm,tt; while(t--) { n=read(); ans=tot=0; memset(tree,-1,sizeof tree); memset(vis,false,sizeof vis); for(int i=1;i<=n;++i) { li[i]=read(),ri[i]=read(); que[tot++]=li[i]; que[tot++]=ri[i]; } sort(que,que+tot); tt=mm=unique(que,que+tot)-que; for(int i=1;i<tt;++i) if(que[i]-que[i-1]>1) que[mm++]=que[i-1]+1; sort(que,que+mm); for(int x,y,i=1;i<=n;++i) { x=lower_bound(que,que+mm,li[i])-que,y=lower_bound(que,que+mm,ri[i])-que; update(x,y,i,0,mm-1,1); } query(0,mm-1,1); printf("%d\n",ans); } return 0; }