jzoj5800. 2018.08.11【2018提高组】模拟A组 被单(线段树合并)

题目描述

Description
一天,Little Donald想要洗干净他的n张被单。洗完所有被单之后,他把它们放在后院的平地上晒干。Donald很好的摆放了这些被单,使得这些被单两两之间不会在端点或边上接触,并且两两之间的边不会相交,但是可能一张更小的被单会放在一张更大的被单上面,或者一张被单会完全覆盖另外一张被单。做完这些事之后,Donald就去睡觉了。然而,Donald的朋友,Kim,知道了Donald正在晒被单并且决定恶搞Donald。他从他爸爸的阁楼找到了一把彩弹枪。在这把枪中有m颗彩弹,可能会有许多种不同颜色的彩弹,同时也可能有许多彩弹是有同一种颜色的。在Donald睡着之后,Kim就进到Donald家的后院并开始用彩弹枪向这些被单射击。每次一张被单被射击之后,这个彩弹的颜色就会染在这张被单以及所有在这张被单下面的被单上。在Kim用完所有的彩弹之后,他就会开心地离开Donald家的后院。
当Donald醒来并且到后院看到自己的被单的时候,他被震惊了。在Donald的大多数被单上面都有许多新的颜色。由于Donald对于正确的数据十分感兴趣,并且他被震惊到无法思考了,所以他想让你告诉他每张被单上面的颜色的数量。
Donald的后院可以用一个平面坐标系表示,被单的边都是平行于坐标轴的,Kim射击的位置可以看成在平面中的一个点。
注意:Kim的一次射击可能不会到任意一个被单上,射击的位置两两之间互不相同。

Input
第一行两个正整数n(1≤n≤80000)和m(1≤m≤80000),分别表示被单的数量和彩弹的数量
接下来n行,这n行中的第i行用四个数字A[i],B[i],C[i],Di描述一张被单,表示这张被单的左下角在(A[i],B[i]),右上角在(C[i],D[i])
接下来m行,这m行中的第i行用三个数字X[i],Y[i],Ki描述一次射击,表示这次射击的位置是在点(X[i],Y[i]),以及这次射出去的彩弹的颜色为K[i]

Output
输出共n行,第i行表示染在第i张被单上面的颜色种数。

Sample Input
Input 1
2 2
1 1 3 3
5 6 10 10
3 3 1
5 1 2
Input 2
3 3
1 1 7 7
2 2 6 6
3 3 5 5
4 4 1
2 6 2
4 7 3
Input 3
1 3
1 1 7 7
2 6 2
4 7 3
4 4 1
Sample Output
Output 1
1
0
Output 2
3
2
1
Output 3
3

100%

比赛时写了200行然后爆0
万恶的捆绑数据


注意到本题有一个重要的性质

这些被单两两之间不会在端点或边上接触,并且两两之间的边不会相交,但是可能一张更小的被单会放在一张更大的被单上面,或者一张被单会完全覆盖另外一张被单

也就是说,被单之间的覆盖关系可以构成一棵树

那么首先可以用扫描线+线段树处理出被单之间的覆盖情况,同时找到每个点所在的最小被单

然后就可以用线段树合并

线段树合并

顾名思义,就是把两颗线段树合并在一起的操作

对于当前合并的两个节点t1t2,根据线段树区间加的性质,要先考虑完它们的子树后再合并
所以分别考虑t1t2的左/右子树(注意是同时考虑左子树,或同时考虑右子树)

①t1t2都没有左/右子树
那么就不用合并了

②t1t2都有左/右子树
同样是因为线段树区间加,不能直接合并,所以要递归到子树中继续搞

③t1t2有一个有左/右子树
因为另一个没有,所以可以直接合并
在实际操作中,并不需要新建节点,只需要改一下子树的编号

等到子树合并完后,再从下往上更新权值

其实并不难,但是我™因为dfs写错调了几个礼拜

时间复杂度

线段树合并的时间复杂度是 O(mlogn) O ( m log ⁡ n ) (m是修改次数,n是范围,实际就是总节点个数)
证明可以用势能分析,我就随便口胡一下

因为一开始有 mlogn m log ⁡ n 个节点,所以势能是 mlogn m log ⁡ n
然后每次把两个节点合并为一个,而且只会合并共有的节点,不会多找
所以每次合并势能-1,时间复杂度+1,最终的复杂度就是 O(mlogn) O ( m log ⁡ n )
纯属口胡

code

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define _len 1000000000
using namespace std;

struct A{
    int x,y1,y2,c,s;
} a2[160001];
struct B{
    int x,y,c;
} b[80001];
int c[80001];
int a[80001][2];
int ls[80001];
int fa[80001];
int tr[5000001][2];
int Tr[3000001][3];
int Ls[5000001];
int d[5000001][2];
bool bz[80001];
int n,m,i,j,k,l,Len,x1,y1,x2,y2,len,Find,len2,len233,len0;

void _new(int x,int y)
{
    len233++;
    a[len233][0]=y;
    a[len233][1]=ls[x];
    ls[x]=len233;
}

void New(int x,int y1,int y2,int c,int s)
{
    Len++;
    a2[Len].x=x;
    a2[Len].y1=y1;
    a2[Len].y2=y2;
    a2[Len].c=c;
    a2[Len].s=s;
}

bool cmp(A a,A b)
{
    return a.x<b.x || a.x==b.x && a.s<b.s;
}

bool Cmp(B a,B b)
{
    return a.x<b.x;
}

bool Cmp2(B a,B b)
{
    return a.c<b.c;
}

void New2(int t,int x)
{
    if (!tr[t][x])
    tr[t][x]=++len;
}

void New3(int t,int x)
{
    if (!Tr[t][x])
    Tr[t][x]=++len2;
}

void New4(int x,int y)
{
    len0++;
    d[len0][0]=y;
    d[len0][1]=Ls[x];
    Ls[x]=len0;
}

void change(int t,int l,int r,int x,int y,int s)
{
    int mid=(l+r)/2;

    if (x<=l && r<=y)
    {
        if (s)
        New4(t,s);
        else
        Ls[t]=d[Ls[t]][1];

        return;
    }

    if (x<=mid)
    {
        New2(t,0);
        change(tr[t][0],l,mid,x,y,s);
    }
    if (mid<y)
    {
        New2(t,1);
        change(tr[t][1],mid+1,r,x,y,s);
    }
}

void find(int t,int l,int r,int x)
{
    int mid=(l+r)/2;

    if (Ls[t]) Find=d[Ls[t]][0];
    if (l==r) return;

    if (x<=mid && tr[t][0])
    find(tr[t][0],l,mid,x);
    else
    if (mid<x  && tr[t][1])
    find(tr[t][1],mid+1,r,x);
}

void Change(int t,int l,int r,int x)
{
    int mid=(l+r)/2;

    if (l==r)
    {
        if (!Tr[t][2])
        Tr[t][2]=1;

        return;
    }

    if (x<=mid)
    {
        New3(t,0);
        Change(Tr[t][0],l,mid,x);
    }
    else
    {
        New3(t,1);
        Change(Tr[t][1],mid+1,r,x);
    }

    Tr[t][2]=Tr[Tr[t][0]][2]+Tr[Tr[t][1]][2];
}

void merge(int t1,int t2,int l,int r)
{
    int mid=(l+r)/2;

    if (l==r) return;

    if (Tr[t1][0] && Tr[t2][0])
    merge(Tr[t1][0],Tr[t2][0],l,mid);
    else
    if (Tr[t2][0])
    Tr[t1][0]=Tr[t2][0];

    if (Tr[t1][1] && Tr[t2][1])
    merge(Tr[t1][1],Tr[t2][1],mid+1,r);
    else
    if (Tr[t2][1])
    Tr[t1][1]=Tr[t2][1];

    Tr[t1][2]=Tr[Tr[t1][0]][2]+Tr[Tr[t1][1]][2];
}

void dfs(int t)
{
    int i;

    bz[t]=1;
    for (i=ls[t]; i; i=a[i][1])
    {
        if (!bz[a[i][0]])
        dfs(a[i][0]);

        merge(t,a[i][0],1,c[m]);
    }
}

int main()
{
    freopen("plahte.in","r",stdin);
    freopen("plahte.out","w",stdout);

    scanf("%d%d",&n,&m);
    fo(i,1,n)
    {
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);

        New(x1,y1,y2,i,1);
        New(x2+1,y1,y2,i,-1);
    }
    sort(a2+1,a2+Len+1,cmp);

    fo(i,1,m)
    scanf("%d%d%d",&b[i].x,&b[i].y,&b[i].c);

    sort(b+1,b+m+1,Cmp2);
    j=0;
    fo(i,1,m)
    {
        j+=(b[i].c!=b[i-1].c);
        c[i]=j;
    }
    fo(i,1,m)
    b[i].c=c[i];

    sort(b+1,b+m+1,Cmp);

    len=1;
    len2=n;
    j=1;
    fo(i,1,m)
    {
        while (j<=Len && a2[j].x<=b[i].x)
        {
            if (a2[j].s==1)
            {
                Find=0;
                find(1,1,_len,a2[j].y1);

                fa[a2[j].c]=Find;

                if (Find)
                _new(Find,a2[j].c);

                change(1,1,_len,a2[j].y1,a2[j].y2,a2[j].c);
            }
            else
            change(1,1,_len,a2[j].y1,a2[j].y2,0);

            j++;
        }

        Find=0;
        find(1,1,_len,b[i].y);

        if (Find)
        Change(Find,1,c[m],b[i].c);
    }

    while (j<=Len)
    {
        if (a2[j].s==1)
        {
            Find=0;
            find(1,1,_len,a2[j].y1);

            fa[a2[j].c]=Find;
            if (Find)
            _new(Find,a2[j].c);

            change(1,1,_len,a2[j].y1,a2[j].y2,a2[j].c);
        }
        else
        change(1,1,_len,a2[j].y1,a2[j].y2,0);

        j++;
    }

    fo(i,1,n)
    if (!bz[i])
    dfs(i);

    fo(i,1,n)
    printf("%d\n",Tr[i][2]);

    fclose(stdin);
    fclose(stdout);

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值