UVALive

来源:互联网 发布:web of science数据库 编辑:程序博客网 时间:2024/06/03 13:55

题目链接: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;}