题目大意:在一个平面上有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;
}