【BZOJ5005】乒乓游戏 [线段树][并查集]
乒乓游戏
Time Limit: 10 Sec Memory Limit: 256 MBDescription
Input
Output
Sample Input
5
1 1 5
1 5 11
2 1 2
1 2 9
2 1 2
Sample Output
NO
YES
HINT
Main idea
如果一个区间的端点在区间内,则这个区间可以走到那个区间,询问一个区间能否到另一个区间。
Source
首先我们立马想到了:如果两个区间严格有交集,那么这两个区间所能到达的区间集合是一样的。那么如果两个区间严格有交集的话我们就可以把它们合并起来,这里运用并查集。
这样处理完之后,剩下的区间只有两种情况:包含或者相离。那么查询的时候显然只要判断两个区间指向的大区间的情况即可。
我们要怎么合并呢?显然就是在线段树上进行操作,对于线段树上的每个节点开个vector,存下严格包含这个节点表示的[l,r]的区间的编号,那么我们加入新区间的时候,只要把左右端点在线段树上往下走,如果遇到这个线段树上的节点上的vector有东西,就记录几个区间的最小左端点以及最大右端点,把这几个区间的父亲都指向这个新区间,再删除掉这几个区间即可。然后合并完之后,把得到的新区间再放到各个点的vector进去。
最后,由于这题区间端点权比较大,所以要先离散化。
Code
1 #include<iostream>
2 #include<string>
3 #include<algorithm>
4 #include<cstdio>
5 #include<cstring>
6 #include<cstdlib>
7 #include<cmath>
8 #include<vector>
9 using namespace std;
10
11 const int ONE=100005*8;
12 const int INF=1e9+1;
13
14 int n;
15 int Num,cnt;
16
17 vector <int> Node[ONE];
18
19 struct power
20 {
21 int l,r,opt;
22 }a[ONE],interval[ONE];
23
24 int Q[ONE],li_num;
25 struct LISAN
26 {
27 int pos,val;
28 }Li[ONE];
29
30 int get()
31 {
32 int res=1,Q=1;char c;
33 while( (c=getchar())<48 || c>57 )
34 if(c=='-')Q=-1;
35 res=c-48;
36 while( (c=getchar())>=48 && c<=57 )
37 res=res*10+c-48;
38 return res*Q;
39 }
40
41 namespace LI
42 {
43 void add(int i)
44 {
45 if(a[i].opt!=1) return;
46 Num++;
47 Li[++li_num].val = a[i].l; Li[li_num].pos = li_num;
48 Li[++li_num].val = a[i].r; Li[li_num].pos = li_num;
49 }
50
51 int cmp(const LISAN &a,const LISAN &b) {return a.val < b.val;}
52 void Lisan()
53 {
54 sort(Li+1, Li+li_num+1, cmp);
55
56 cnt=0;
57 Li[0].val=-INF;
58 for(int i=1;i<=li_num;i++)
59 {
60 if(Li[i].val!=Li[i-1].val) ++cnt;
61 Q[Li[i].pos]=cnt;
62 }
63 Num=cnt;
64
65 cnt=0;
66 for(int i=1;i<=n;i++)
67 if(a[i].opt==1)
68 a[i].l=Q[++cnt], a[i].r=Q[++cnt];
69
70 }
71 }
72
73 int fat[ONE];
74 int Find(int x)
75 {
76 if(fat[x]==x) return x;
77 return fat[x]=Find(fat[x]);
78 }
79
80 namespace Seg
81 {
82 void Delete(int i,int l,int r,int L)
83 {
84 if(Node[i].size())
85 {
86 for(int j=0; j<Node[i].size(); j++)
87 {
88 int id=Node[i][j];
89 fat[ Find(id) ] = cnt;
90 interval[cnt].l = min(interval[cnt].l, interval[id].l);
91 interval[cnt].r = max(interval[cnt].r, interval[id].r);
92 }
93 Node[i].clear();
94 }
95 if(l==r) return;
96 int mid = (l+r)>>1;
97 if(L <= mid) Delete(i<<1, l, mid, L);
98 else Delete(i<<1|1, mid+1, r, L);
99 }
100
101 void Update(int i,int l,int r,int L,int R)
102 {
103 if(L>R) return;
104 if(L<=l && r<=R)
105 {
106 Node[i].push_back(cnt);
107 return;
108 }
109 int mid=(l+r)>>1;
110 if(L<=mid) Update(i<<1,l,mid,L,R);
111 if(mid+1<=R) Update(i<<1|1,mid+1,r,L,R);
112 }
113 }
114
115 bool P_edge(power a,power b)
116 {
117 if( (b.l<a.l && a.l<b.r) || (b.l<a.r && a.r<b.r)) return 1;
118 return 0;
119 }
120
121 int main()
122 {
123 n=get();
124 for(int i=1;i<=n;i++)
125 {
126 a[i].opt=get();
127 a[i].l=get(); a[i].r=get();
128 LI::add(i);
129 }
130 for(int i=1;i<=Num;i++) fat[i]=i;
131
132 LI::Lisan();
133
134 cnt=0;
135 for(int i=1;i<=n;i++)
136 {
137 if(a[i].opt==1)
138 {
139 interval[++cnt] = a[i];
140 Seg::Delete(1,1,Num, a[i].l);
141 Seg::Delete(1,1,Num, a[i].r);
142 Seg::Update(1,1,Num, interval[cnt].l+1,interval[cnt].r-1);
143 }
144 else
145 {
146 int x=Find(a[i].l), y=Find(a[i].r);
147 if(x==y || P_edge(interval[x] , interval[y]))
148 printf("YES\n");
149 else
150 printf("NO\n");
151 }
152 }
153 }