【JZOJ5800】【洛谷P4416】 [COCI2017-2018#1] 被单(set启发式合并/线段树合并)

Problem

  • 给定n(≤80000)个矩形,矩形不会重点、重边或相交。给出m(≤80000)个有颜色的点。求每个矩形上面的点的颜色种数。

Solution

  • 前置技能:set启发式合并/线段树合并。

  • 既然矩形不会相交,那只有可能一个包含一个。记一个矩形的父亲为包含它的所有矩形中最小的那个,则原图可化为一棵森林。
  • 连接这个森林需要用扫描线。我们将矩形的左边界、右边界按x坐标排序,然后扫一遍。
  • 我们对y轴建一棵标记永久化的线段树。扫到左边界时,直接把标记打到相应区间。
  • 那么我们若要查询当前矩形的父亲,就直接查询相应区间中的任意一点。比如说是区间[l,r],我们不必担心此时有多个矩形占据了这个区间,或者有矩形占据了部分,因为不可能会出现下图的情况:
    【JZOJ5800】【洛谷P4416】 [COCI2017-2018#1] 被单(set启发式合并/线段树合并)_第1张图片
  • 如图,我们若要找3的父亲,就是找线段树中的蓝色区间。蓝色区间绝不可能有矩形只占据了部分,也绝不可能有多个矩形占据,因为这样就会导致上图所示的矩形相交。

  • 我们在查询的时候,不用下传标记。因为若一个点被打上了标记,那么这段区间(线段树一个点代表一个区间)均被标记的矩形占据。因此,只要查到了标记,直接return。
  • 同理,我们也可以如此查询包含一个点的第一个矩形(最小的那个矩形)。

  • 不过这样有一个问题,那就是扫到矩形的右边界时,不能直接区间赋为0——毕竟还有其他的矩形可能会占据那块位置。
  • 实际上,我们把相应区间赋为该矩形的父亲即可。因为除了它,占据那块区间的最小矩形便是它父亲。

  • 对于每个点,它会在第一个包含它的矩形及其祖先的留下颜色。
  • 我们直接对每个矩形建立一棵权值线段树or set,暂存个中出现的颜色。
  • 一开始,每个点直接打在第一个包含它的矩形中;然后我们dfs整棵树,做完子节点后将儿子与自己合并。

  • 发现本题有n棵线段树,并且元素和为m,因此线段树合并的时间复杂度为 O(n+mlog2m) O ( n + m ∗ l o g 2 m )
  • 如果是set启发式合并的话,就有点玄了。不过如果它是一棵近似的完全二叉树,并且叶子节点各含一种颜色,那么就是 O(n+mlog2mlog2n) 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启发式合并的以后再补吧。。。

线段树合并

#include 
#define A v<<1
#define B A|1
#define fo(i,a,b) for(i=a;i<=b;i++)
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]);
}

你可能感兴趣的:(-------data,Segment,Tree,启发式合并)