UVALive 4730 -树状数组+带权并查集


题意:有T组测试数据,每组数据的N表示有N个城市,接下来的N行里每行给出每个城市的坐标(0<=x,y<=1000000),然后有M(1<M<200000)个操作,操作有两类,

1)"road A B",表示将城市A和城市B通过一条道路连接,如果A和B原来属于不同的城市群,经过这个操作,A和B就在一个城市群里了,保证每条道路不会和其他道路相交(除了端点A和B)。

(2)"line C",表示查询当穿过y=C的直线,有多少个城市群、这几个城市群一共有多少个城市。


以C建立一颗线段树,

 每次 road A,B 操作,会新生成一个洲,那么对于这个洲,我们用并查集维护其城市数量num,和其最下、最上延伸范围sonl,sonr,也就是说这个新的洲会覆盖 【sonl,sonr】这个范围,那么我们在一颗树状数组里 这个范围的 洲数量 add 1, 另一个树状数组里(管辖城市数量的),add num

ps:当前生成之前的,A,B所在的洲要先抹去掉。


对于查询line C,直接分别在两棵树分别查询两个信息即可


范围有点大,离散化一下即可。





#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#include <vector>
using namespace std;
#define  inf 0x7fffffff
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
int n,m;
class Tree
{
    int tree[100131+50];
public:
    void init()
    {
        memset(tree,0,sizeof tree);
    }
    int lowbit(int x)
    {
        return x&-x;
    }
    void add(int x,int value)
    {
        for (int i=x; i<=100000; i=i+lowbit(i))
        {
            tree[i]+=value;
        }
    }
    int get(int x)
    {
        int sum=0;
        for (int i=x; i; i-=lowbit(i))
        {
            sum+=tree[i];
        }
        return sum;
    }

};
Tree tp1,tp2;
int fa[100132];
int sonl[100132];
int sonr[100131];
int num[100132];
int find(int x)
{
    if (x==fa[x]) return x;
    else  return fa[x]=find(fa[x]);
}
int city[100132][2];
void Union(int x,int y)
{
    int fx=find(x);
    int fy=find(y);
    if (fx==fy) return;
    tp1.add(sonl[fx],-1);
    tp1.add(sonr[fx]+1-1,1);
    tp1.add(sonl[fy],-1);
    tp1.add(sonr[fy]+1-1,1);
    tp2.add(sonl[fx],-num[fx]);
    tp2.add(sonr[fx]+1-1,num[fx]);
    tp2.add(sonl[fy],-num[fy]);
    tp2.add(sonr[fy]+1-1,num[fy]);

    sonl[fy]=min(sonl[fy],sonl[fx]);
    sonr[fy]=max(sonr[fy],sonr[fx]);
    num[fy]+=num[fx];
    fa[fx]=fy;
    tp1.add(sonl[fy],1);
    tp1.add(sonr[fy]+1-1,-1);
    tp2.add(sonl[fy],num[fy]);
    tp2.add(sonr[fy]+1-1,-num[fy]);
    return  ;
}
int bb[100132];
int idx[100132];
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        tp1.init();
        tp2.init();
        cin>>n;
        int x,y;
        char tmp[15];

        for (int i=1; i<=n; i++)
            scanf("%d%d",&city[i][0],&city[i][1]);
        for (int i=1; i<=n; i++ ) bb[i]=city[i][1];
        sort(bb+1,bb+1+n);
        int cun=unique(bb+1,bb+1+n)-bb-1;
        for (int i=1;i<=n;i++)
        {
            int id=lower_bound(bb+1,bb+1+cun,city[i][1])-bb;
            idx[i]=id;
        }
        for (int i=1; i<=n; i++) fa[i]=i,sonl[i]=sonr[i]=idx[i],num[i]=1;
        int m;
        cin>>m;
        for (int i=1; i<=m; i++)
        {
            scanf("%s",tmp);
            if (tmp[0]=='r')
            {
                scanf("%d%d",&x,&y);
                x++,y++;
                Union(x,y);
            }
            else
            {
                scanf("%d.5",&x);
                x=upper_bound(bb+1,bb+1+cun,x)-bb;
                x--;
                int ans1=tp1.get(x);
                int ans2=tp2.get(x);
                printf("%d %d\n",ans1,ans2);
            }
        }
    }


    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值