Problem
给定n(≤80000)个矩形,矩形不会重点、重边或相交。给出m(≤80000)个有颜色的点。求每个矩形上面的点的颜色种数。
Solution
既然矩形不会相交,那只有可能一个包含一个。记一个矩形的父亲为包含它的所有矩形中最小的那个,则原图可化为一棵森林。 连接这个森林需要用扫描线。我们将矩形的左边界、右边界按x坐标排序,然后扫一遍。 我们对y轴建一棵标记永久化的线段树。扫到左边界时,直接把标记打到相应区间。 那么我们若要查询当前矩形的父亲,就直接查询相应区间中的任意一点。比如说是区间[l,r],我们不必担心此时有多个矩形占据了这个区间,或者有矩形占据了部分,因为不可能会出现下图的情况: 如图,我们若要找3的父亲,就是找线段树中的蓝色区间。蓝色区间绝不可能有矩形只占据了部分,也绝不可能有多个矩形占据,因为这样就会导致上图所示的矩形相交。
我们在查询的时候,不用下传标记。因为若一个点被打上了标记,那么这段区间(线段树一个点代表一个区间)均被标记的矩形占据。因此,只要查到了标记,直接return。 同理,我们也可以如此查询包含一个点的第一个矩形(最小的那个矩形)。
不过这样有一个问题,那就是扫到矩形的右边界时,不能直接区间赋为0——毕竟还有其他的矩形可能会占据那块位置。 实际上,我们把相应区间赋为该矩形的父亲即可。因为除了它,占据那块区间的最小矩形便是它父亲。
对于每个点,它会在第一个包含它的矩形及其祖先的留下颜色。 我们直接对每个矩形建立一棵权值线段树or set,暂存个中出现的颜色。 一开始,每个点直接打在第一个包含它的矩形中;然后我们dfs整棵树,做完子节点后将儿子与自己合并。
发现本题有n棵线段树,并且元素和为m,因此线段树合并的时间复杂度为
O ( n + m ∗ l o g 2 m )
O
(
n
+
m
∗
l
o
g
2
m
)
。 如果是set启发式合并的话,就有点玄了。不过如果它是一棵近似的完全二叉树,并且叶子节点各含一种颜色,那么就是
O ( n + m ∗ l o g 2 m ∗ l o g 2 n )
O
(
n
+
m
∗
l
o
g
2
m
∗
l
o
g
2
n
)
。因为完全二叉树共
l o g 2 n
l
o
g
2
n
层,每层的节点总数为m,因此每层我们要将包含m/2个点的一堆set合并到包含m/2个点的另一堆set中。
Code
目前我只打了线段树合并的,set启发式合并的以后再补吧。。。
线段树合并
using namespace std;
const int N=8 e4+1 ;
int i,n,m ,a,b,c,d,ts,x ,y ,k,ys,Y[N<<2 ],ks,K[N],fa[N];
struct T
{
int x ,y ,k,i;
inline bool operator<(const T a)const{return x <a.x |x ==a.x &i>a.i;}
}o[N<<2 ];
struct edge
{
int v;
edge *ne ;
edge(int v,edge *ne ):v(v),ne (ne ){}
}*fin [N];
void read (int &x )
{
char ch=getchar(); x =0 ;
while (!isdigit(ch)) ch=getchar();
while (isdigit(ch)) x =(x <<3 )+(x <<1 )+(ch^48 ), ch=getchar();
}
int px,py,pv,t[N<<3 ];
int query(int v,int l,int r)
{
if (t[v]>=0 |l==r) return t[v];
if (!v) return 0 ;
int mid=l+r>>1 ;
return px<=mid ? query(A,l,mid) : query(B,mid+1 ,r);
}
void push (int v)
{
if (t[v]<0 ) return ;
t[A]=t[B]=t[v];
t[v]=-1 ;
}
void modify(int v,int l,int r)
{
if (px<=l&&r<=py){t[v]=pv; return ;}
push (v);
int mid=l+r>>1 ;
if (px<=mid) modify(A,l,mid);
if (py>mid) modify(B,mid+1 ,r);
}
int to,rt[N],col[N<<5 ],so[N<<5 ][2 ],ans[N];
void add(int &v,int l,int r)
{
if (!v) v=++to;
if (l==r) {col[v]=1 ; return ;}
int mid=l+r>>1 ;
px<=mid ? add(so[v][0 ],l,mid) : add(so[v][1 ],mid+1 ,r);
col[v]=col[so[v][0 ]]+col[so[v][1 ]];
}
int merge(int a,int b,int l,int r)
{
if (!a|!b) return a^b;
if (l==r) return a;
int mid=l+r>>1 ;
so[a][0 ]=merge(so[a][0 ],so[b][0 ],l,mid);
so[a][1 ]=merge(so[a][1 ],so[b][1 ],mid+1 ,r);
col[a]=col[so[a][0 ]]+col[so[a][1 ]];
return a;
}
inline void link (int x ,int y )
{
fin[x ]=new edge{y ,fin[x ]};
}
void dfs(int x )
{
int y ;
for (edge *i =fin[x ]; i; i=i->ne )
if ((y =i->v)!=fa[x ])
dfs(y ), rt[x ]=merge(rt[x ],rt[y ],1 ,ks);
ans[x ]=col[rt[x ]];
}
int main()
{
freopen("plahte.in" ,"r" ,stdin);
freopen("plahte.out" ,"w" ,stdout);
read (n); read (m );
fo(i,1 ,n)
{
read (a); read (b); read (c); read (d);
o[++ts]={a,b,d, i}; Y[++ys]=b;
o[++ts]={c,b,d,-i}; Y[++ys]=d;
}
fo(i,1 ,m )
{
read (x ); read (y ); read (k);
o[++ts]={x ,y ,k,0 };
Y[++ys]=y ; K[++ks]=k;
}
sort (Y+1 ,Y+ys+1 ); ys=unique(Y+1 ,Y+ys+1 )-Y-1 ;
sort (K+1 ,K+ks+1 ); ks=unique(K+1 ,K+ks+1 )-K-1 ;
sort (o+1 ,o+ts+1 );
memset(t,200 ,sizeof t);
fo(i,1 ,ts)
{
o[i].y =lower_bound(Y+1 ,Y+ys+1 ,o[i].y )-Y;
if (o[i].i)
{
o[i].k=lower_bound(Y+1 ,Y+ys+1 ,o[i].k)-Y;
if (o[i].i>0 )
{
px=o[i].y ; py=o[i].k; pv=o[i].i;
if ((fa[pv]=query(1 ,1 ,ys))>0 ) link (fa[pv],pv);
modify(1 ,1 ,ys);
continue ;
}
px=o[i].y ; py=o[i].k; pv=max(fa[-o[i].i],0 );
modify(1 ,1 ,ys);
continue ;
}
o[i].k=lower_bound(K+1 ,K+ks+1 ,o[i].k)-K;
px=o[i].y ; int pos =query(1 ,1 ,ys);
if (pos >0 ) px=o[i].k, add(rt[pos ],1 ,ks);
}
fo(i,1 ,n) if (fa[i]<=0 ) dfs(i);
fo(i,1 ,n) printf ("%d \n" ,ans[i]);
}