UVALive - 4730 Kingdom(并查集 + 线段树区间更新)

题目链接:http://https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2731

题目大意:在一个平面上有n个城市,初始时的城市之间没有任何双向道路相连。你的任务是依次执行以下两种指令:

1、road A B:在城市A和城市B之间连接一条双向道路,保证这条道路不和其他道路在这非端点处相交;

2、 line C : 询问一条y=C的水平线和多少个州相交,以及这些州一共包括多少座城市。在任意时刻,每一组连通的城市形成一个州。(C的小数部分保证为0.5)。

题目思路:因为要求联通块的数量以及联通块中点的数量,自然就想到了用并查集来维护。对于求解方法可以建立两棵以y轴的坐标为下标的线段树,一棵维护联通块数量,一棵维护点的数量。在用并查集维护的点同时可以维护一下当前联通块的上届up和下届down,则在线段树的[down,up]区间内所有的联通块数量就等于所有上届和下届在[down,up]区间之内的联通块的数量和,城市数量也是如此。最后再用一个查询,查出C所对应的线段树的下标即可找到答案。具体实现看代码。

AC代码如下:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define fuck(x) cout<<'['<<x<<']'<<endl
#define FIN freopen("in.txt","r",stdin)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long LL;
typedef pair<int, int>pii;
const int MX = 1e6 + 7;
const int maxn = 1e5 + 7;

int T,n,m;
int sum1[MX<<2],sum2[MX<<2],add1[MX<<2],add2[MX<<2];
int P[maxn],up[maxn],down[maxn],num[maxn];
//up[i]表示第i个联通块的上界,down[i]表示第i个联通块的下界,num[i]表示第i个联通块中包含的城市的数量;

int found(int x){
    return P[x] == x ? x : (P[x] = found(P[x]));
}

void push_up(int rt){
    sum1[rt] = sum1[rt<<1] + sum1[rt<<1|1];//州的数量;
    sum2[rt] = sum2[rt<<1] + sum2[rt<<1|1];//城市的数量;
}

void push_down(int rt){
    if(add1[rt]){
        add1[rt<<1] += add1[rt];
        add1[rt<<1|1] += add1[rt];
        sum1[rt<<1] += add1[rt];
        sum1[rt<<1|1] += add1[rt];
        add1[rt] = 0;
    }
    if(add2[rt]){
        add2[rt<<1] += add2[rt];
        add2[rt<<1|1] += add2[rt];
        sum2[rt<<1] += add2[rt];
        sum2[rt<<1|1] += add2[rt];
        add2[rt] = 0;
    }
}

void update(int op,int L,int R,int d,int l,int r,int rt){
    if(L > R) return;
    if(L <= l && r <= R){
        if(!op){
            //更新区间中州的数量;
            add1[rt] += d;
            sum1[rt] += d;
        } else{
            //更新区间中城市的数量;
            add2[rt] += d;
            sum2[rt] += d;
        }
        return;
    }
    int m = (l + r) >> 1;
    push_down(rt);
    if(L <= m) update(op,L,R,d,lson);
    if(R > m) update(op,L,R,d,rson);
    push_up(rt);
}

int query(int p,int l,int r,int rt){
    if(l == r) return rt;
    push_down(rt);
    int m = (l + r) >> 1;
    if(p <= m) return query(p,lson);
    else return query(p,rson);
    push_up(rt);
}

void init(){
    mem(sum1,0);mem(sum2,0);
    mem(add1,0);mem(add2,0);
    for(int i = 0;i <= n;i++) P[i] = i;
}

void Union(int u,int v,int Max){
    int uu = found(u);
    int vv = found(v);
    if(uu != vv){
        if(up[uu] > up[vv])
            swap(uu,vv);//保证u所在的联通块的上界小于v所在的联通块的上界以便后面的比较;
        if(down[vv] > up[uu]){//当两个联通块无交集的情况;
            update(0,up[uu]+1,down[vv],1,1,Max,1);
            //更新u所在的联通块的上界到v所在联通块的下界之间的区间联通块的数量;
            update(1,up[uu]+1,down[vv],num[uu]+num[vv],1,Max,1);
            //更新u所在的联通块的上界到v所在联通块的下界之间的区间城市的数量;
            update(1,down[uu]+1,up[uu],num[vv],1,Max,1);
            //更新u所在的联通块所在的区间的城市的数量;
            update(1,down[vv]+1,up[vv],num[uu],1,Max,1);
            //更新v所在的联通块所在的区间的城市的数量;
        } else if(down[uu] > down[vv]){//u所在的联通块完全在v所在的联通块的区间内;
            update(0,down[uu]+1,up[uu],-1,1,Max,1);
            //联通后,u所在的区间的联通块的数量-1;
            update(1,down[vv]+1,down[uu],num[uu],1,Max,1);
            //更新v所在的联通块的下界到u所在联通块的下界之间的区间城市的数量;
            update(1,up[uu]+1,up[vv],num[uu],1,Max,1);
            //更新u所在的联通块的上界到v所在联通块的上界之间的区间城市的数量;
        } else{//u所在的联通块一部分在v的联通块的区间内;
            update(0,down[vv]+1,up[uu],-1,1,Max,1);
            //联通后,v所在的联通块下界到u所在的联通块的上界的区间内的城市的数量-1;
            update(1,down[uu]+1,down[vv],num[vv],1,Max,1);
            //更新u所在的联通块的下界到v所在联通块的下界之间的区间城市的数量;
            update(1,up[uu]+1,up[vv],num[uu],1,Max,1);
            //更新u所在的联通块的上界到v所在联通块的上界之间的区间城市的数量;
        }
        P[uu] = vv;//更新联通块;
        num[vv] += num[uu];//更新联通块内点的数量
        down[vv] = min(down[uu],down[vv]);//更新联通块的下界;
        up[vv] = max(up[uu],up[vv]);//更新联通块的上界;
    }
}

int main() {
    //FIN;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        init();
        int Max = 0;
        for(int i = 0;i < n;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            up[i] = down[i] = y;
            num[i] = 1;
            Max = max(Max,y);
        }
        Max++;
        scanf("%d",&m);
        while(m--){
            char op[10];
            scanf("%s",op);
            if(op[0] == 'r'){
                int u,v;
                scanf("%d%d",&u,&v);
                Union(u,v,Max);
            } else{
                double line;
                scanf("%lf",&line);
                int p = query((int)(line+1),1,Max,1);//查询C所在的坐标的线段树下标;
                printf("%d %d\n",sum1[p],sum2[p]);
            }
        }
    }
    return 0;
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值