1 /*
2 poj2528
3 线段树 好题,用到了离散化,二分定位,特殊的区间查寻方式;在下面的代码注释中有详细的解释;
4 */
5 #include<iostream>
6 #include<cstdio>
7 #include<cstring>
8 #include<algorithm>
9
10 using namespace std;
11 typedef struct
12 {
13 int l,r;
14 int cnt;//记录该段有几张海报;
15 int num;//记录该段下面是哪一张海报,0表示没有还海报或海报不唯一;
16 }Node;
17 int a[400010][2],n,dot[400010]={0},cou[400010]={0};
18 Node tr[800010];
19 void build(int rt,int l,int r)
20 {
21 tr[rt].l=l;
22 tr[rt].r=r;
23 tr[rt].cnt=0;
24 tr[rt].num=0;
25 if(l==r)
26 return ;
27 int mid=(l+r)/2;
28 build(rt<<1,l,mid);
29 build((rt<<1)|1,mid+1,r);
30 }
31 void Pushdown(int rt)
32 {
33 tr[rt<<1].cnt=1;
34 tr[rt<<1|1].cnt=1;
35 tr[rt<<1].num=tr[rt].num;
36 tr[rt<<1|1].num=tr[rt].num;
37 tr[rt].cnt=2;
38 tr[rt].num=0;
39 }
40 void Update(int rt,int l,int r,int x)
41 {
42 if(tr[rt].l==l&&tr[rt].r==r)
43 {
44 tr[rt].cnt=1;
45 tr[rt].num=x;
46 return;
47 }
48 if(tr[rt].cnt==1)//当只有一张海报在这个区间的时候才需要pushdown所以可以判断一下;也可以写在pushdown函数里面,我觉得写在外面比较块;
49 Pushdown(rt);
50 if(l<=tr[rt<<1].r)
51 {
52 if(r<=tr[rt<<1].r)
53 Update(rt<<1,l,r,x);
54 else
55 Update(rt<<1,l,tr[rt<<1].r,x);
56 }
57 if(r>=tr[rt<<1|1].l)
58 {
59 if(l>=tr[rt<<1|1].l)
60 Update(rt<<1|1,l,r,x);
61 else
62 Update(rt<<1|1,tr[rt<<1|1].l,r,x);
63 }
64 if(tr[rt<<1].num==tr[rt<<1|1].num&&tr[rt<<1].cnt==1&&tr[rt<<1|1].cnt==1)//这以下是pushup的操作,写在这里了;
65 {
66 tr[rt].cnt=1;
67 tr[rt].num=tr[rt<<1].num;
68 }
69 else
70 tr[rt].cnt=2;
71 }
72 /*
73 查询操作比较有趣,因为是查寻区间的海报种类,一个海报可能是首尾可见而中间不可见,所以没办法直接查得,
74 所以在树的结点上记录的是海报的编号,这样就可以用一个辅助的数组记录哪一个在此次查询中出现过;、
75 所以每次查询要全区间查询,然后再扫一遍数组;
76 、每次扫到区间内容一质的时候就不用再扫了;
77 */
78 void Query(int rt)
79 {
80 if(tr[rt].cnt==1)
81 {
82 cou[tr[rt].num]=1;
83 return ;
84 }
85 if(tr[rt].l==tr[rt].r)
86 return ;
87 if(tr[rt].cnt==0)
88 return ;
89 Query(rt<<1);
90 Query(rt<<1|1);
91 }
92 int Erfen(int l,int r,int x)//用来确定该点在线段上的位置(离散后的位置);
93 {
94 while(l<=r)
95 {
96 int mid=(l+r)/2;
97 if(x<dot[mid])
98 r=mid-1;
99 else
100 l=mid+1;
101 }
102 return r;
103 }
104 int main()
105 {
106 int T;
107 scanf("%d",&T);
108 while(T--)
109 {
110 memset(dot,0,sizeof(dot));
111 scanf("%d",&n);
112 int z=0;
113 for(int i=0;i<n;i++)
114 {
115 scanf("%d%d",&a[i][0],&a[i][1]);
116 dot[z++]=a[i][0];
117 dot[z++]=a[i][1];
118 }
119 sort(dot,dot+z);//将输入的数据排序后是离散化;因为数据范围比较大,因为只和区间有关,与区间内部的点没有关系,所以只要区间首尾就好;
120 int t=1;
121 for(int i=1;i<z;i++)
122 {
123 if(dot[i]!=dot[i-1])
124 dot[t++]=dot[i];
125 }
126 for(int i=t-1;i>0;i--)
127 {
128 if(dot[i]-dot[i-1]>1)
129 dot[t++]=dot[i-1]+1;
130 }
131 sort(dot,dot+t);
132 for(int i=t;i>0;i--)
133 dot[i]=dot[i-1];
134 memset(tr,0,sizeof(tr));
135 build(1,1,t+5);
136 for(int i=1;i<=n;i++)
137 {
138 int temp1=Erfen(1,t,a[i-1][0]);
139 int temp2=Erfen(1,t,a[i-1][1]);
140 Update(1,temp1,temp2,i);
141 }
142 memset(cou,0,sizeof(cou));
143 Query(1);
144 int ans=0;
145 for(int i=1;i<=n;i++)
146 if(cou[i]==1)
147 ans++;
148 printf("%d\n",ans);
149 }
150 return 0;
151 }