Problem
- 给定n(≤80000)个矩形,矩形不会重点、重边或相交。给出m(≤80000)个有颜色的点。求每个矩形上面的点的颜色种数。
Solution
- 既然矩形不会相交,那只有可能一个包含一个。记一个矩形的父亲为包含它的所有矩形中最小的那个,则原图可化为一棵森林。
- 连接这个森林需要用扫描线。我们将矩形的左边界、右边界按x坐标排序,然后扫一遍。
- 我们对y轴建一棵标记永久化的线段树。扫到左边界时,直接把标记打到相应区间。
- 那么我们若要查询当前矩形的父亲,就直接查询相应区间中的任意一点。比如说是区间[l,r],我们不必担心此时有多个矩形占据了这个区间,或者有矩形占据了部分,因为不可能会出现下图的情况:
- 如图,我们若要找3的父亲,就是找线段树中的蓝色区间。蓝色区间绝不可能有矩形只占据了部分,也绝不可能有多个矩形占据,因为这样就会导致上图所示的矩形相交。
- 我们在查询的时候,不用下传标记。因为若一个点被打上了标记,那么这段区间(线段树一个点代表一个区间)均被标记的矩形占据。因此,只要查到了标记,直接return。
- 同理,我们也可以如此查询包含一个点的第一个矩形(最小的那个矩形)。
- 不过这样有一个问题,那就是扫到矩形的右边界时,不能直接区间赋为0——毕竟还有其他的矩形可能会占据那块位置。
- 实际上,我们把相应区间赋为该矩形的父亲即可。因为除了它,占据那块区间的最小矩形便是它父亲。
- 对于每个点,它会在第一个包含它的矩形及其祖先的留下颜色。
- 我们直接对每个矩形建立一棵权值线段树or set,暂存个中出现的颜色。
- 一开始,每个点直接打在第一个包含它的矩形中;然后我们dfs整棵树,做完子节点后将儿子与自己合并。
- 发现本题有n棵线段树,并且元素和为m,因此线段树合并的时间复杂度为 O(n+m∗log2m) O ( n + m ∗ l o g 2 m ) 。
- 如果是set启发式合并的话,就有点玄了。不过如果它是一棵近似的完全二叉树,并且叶子节点各含一种颜色,那么就是 O(n+m∗log2m∗log2n) O ( n + m ∗ l o g 2 m ∗ l o g 2 n ) 。因为完全二叉树共 log2n l o g 2 n 层,每层的节点总数为m,因此每层我们要将包含m/2个点的一堆set合并到包含m/2个点的另一堆set中。
Code
- 目前我只打了线段树合并的,set启发式合并的以后再补吧。。。
线段树合并
using namespace std;
const int N=8e4+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 xx|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]);
}