题意
给出n个区间,n<=10000, 依次将每个区间涂上不同 的颜色,问最后还能看见多少种颜色
输入
第一行是一个整数t表示t组数据 对于每组数据: 第1行是n表示有n个区间 接下来n行2个整数L,R表示区间(1<=L<=R<=10000000)
分析
从区间的问题很明显的看出是线段树,做法也很简单,将没有颜色的点标为-1,有多种颜色的区间标为0,有一种颜色的用1~n代表颜色,最后查询出整个区间的颜色数量就行了
但是问题在于区间的范围很大,数组无法承受
所以是一个基础的离散化
离散化的方法简单来说就是通过排序,保持序列l和r的有序性,将排序后的顺序作为新的序号,这样有效地降低了数值大小。
需要注意的是,如果说两端点中间还有其他数,那必须再生成一个节点。
比如 这三对数:1 10 1 3 5 10 答案应该是3,但是离散后答案会变成2,这就是因为中间没生成一个点来代表两数之间的数的原因。
通过unique去重,lower_bound查找要用的节点离散后的位置即可。
#include<bits/stdc++.h> using namespace std; #define N 2020000 #define lc (p<<1) #define rc (p<<1|1) #define mid (t[p].l+t[p].r>>1) int T,n,m,tot,ans; int col[N],num[N],a[N],ls[N],rs[N]; struct email { int l,r,lazy,c; }t[N*4]; inline void pushnow(int p,int c) { t[p].lazy=c; t[p].c=c; } inline void pushup(int p) { if(!t[lc].c||!t[rc].c||t[lc].c!=t[rc].c) t[p].c=0; else t[p].c=t[rc].c; } inline void pushdown(int p) { if(t[p].lazy!=-1) { pushnow(lc,t[p].lazy); pushnow(rc,t[p].lazy); t[p].lazy=-1; } } void build(int p,int l,int r) { t[p].l=l,t[p].r=r; if(l==r) { t[p].c=-1; t[p].lazy=-1; return ; } build(lc,l,mid);build(rc,mid+1,r); pushup(p); } void update(int p,int ql,int qr,int c) { if(ql<=t[p].l&&t[p].r<=qr) { pushnow(p,c); return; } pushdown(p); if(ql<=mid)update(lc,ql,qr,c); if(qr>mid)update(rc,ql,qr,c); pushup(p); } void query(int p,int ql,int qr) { if(t[p].c==-1) return ; else if(t[p].c>0) { col[t[p].c]=1; return ; } pushdown(p); if(ql<=mid) query(lc,ql,qr); if(qr>mid) query(rc,ql,qr); } int main() { scanf("%d",&T); while(T--) { ans=0; scanf("%d",&n); for(int i=1;i<=n;i++) { int pl,pr; scanf("%d%d",&pl,&pr); ls[i]=pl;rs[i]=pr; num[i*2-1]=pl;num[i*2]=pr; } sort(num+1,num+1+2*n); m=unique(num+1,num+1+2*n)-(num+1); tot=m; for(int i=1;i<m;i++) if(num[i]+1<num[i+1]) num[++tot]=num[i]+1; sort(num+1,num+1+tot); build(1,1,tot); memset(col,0,sizeof(col)); for(int i=1;i<=n;i++) { int x=lower_bound(num+1,num+1+tot,ls[i])-num; int y=lower_bound(num+1,num+1+tot,rs[i])-num; update(1,x,y,i); } query(1,1,tot); for(int i=1;i<=n;i++) if(col[i]) ans++; printf("%d ",ans); } return 0; }