题意:有n个带坐标的城市,和m个操作,每次操作: road A B,连接A,B城市成为一个州 或者 line C,查询y=C的水平线和多少个州相交以及这些州一共包含多少个城市。
思路:可以把每个城市的x坐标忽略,把纵坐标看做线段树的区间,一个点就相当于更新区间[y,y],一条线就相当于更新区间[y1,y2],每次查询y=C相当于查点C,那么这题转化成区间更新,求点的线段树问题,如果有多个点连接成一个图,那么只要这个图包含最大的纵坐标和最小的纵坐标当成区间去更新即可,那么这个图有多少个城市呢,就需要用并查集算法来记录。如果连接A,B,那么用并查集路径压缩找到A,B的根节点,如果同根,不需要更新,不同根,从线段树中删除这两个点的信息,然后把这两个点的根节点相连并更新新的根节点的节点数以及区间的范围,然后再把新的根节点插入线段树即可。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e5+5;
struct node
{
int y1,y2,sum,to;
}a[maxn];
int sum1[maxn*60],sum2[maxn*60],ans1,ans2;
void build(int l,int r,int o)
{
if(l==r)
{
sum1[o]=sum2[o]=0;
return;
}
int m=(l+r)/2;
build(l,m,o*2);
build(m+1,r,o*2+1);
sum1[o]=sum2[o]=0;
}
void update(int l,int r,int o,int k,int flag)
{
if(l>=a[k].y1&&r<=a[k].y2)
{
if(flag==1)//flag=1删除,否则插入
{
sum1[o]++;
sum2[o]+=a[k].sum;
}
else
{
sum1[o]--;
sum2[o]-=a[k].sum;
}
return;
}
int m=(l+r)/2;
if(a[k].y1<=m)
update(l,m,o*2,k,flag);
if(a[k].y2>m)
update(m+1,r,o*2+1,k,flag);
}
int find(int x)
{
if(a[x].to!=x)
a[x].to=find(a[x].to);
return a[x].to;
}
void query(int l,int r,int o,int C)
{
ans1+=sum1[o];
ans2+=sum2[o];
if(l==r)
return;
int m=(l+r)/2;
if(C<=m)
query(l,m,o*2,C);
else
query(m+1,r,o*2+1,C);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int i,n,l=2e6,r=0;
float t1,t2;
scanf("%d",&n);
for(i=0;i<n;i++)
{
scanf("%f%f",&t1,&t2);
a[i].y1=a[i].y2=t2*2;
a[i].sum=1;
a[i].to=i;
l=min(a[i].y2,l);
r=max(a[i].y2,r);
}
build(l,r,1);
for(i=0;i<n;i++)
update(l,r,1,i,1);
int q,A,B,C;
char s[10];
scanf("%d",&q);
while(q--)
{
scanf("%s",s);
if(s[0]=='r')
{
scanf("%d%d",&A,&B);
A=find(A);
B=find(B);
if(A==B)
continue;
update(l,r,1,A,2);
update(l,r,1,B,2);
a[B].sum+=a[A].sum;
a[B].y2=max(a[B].y2,a[A].y2);
a[B].y1=min(a[B].y1,a[A].y1);
a[A].to=B;
update(l,r,1,B,1);
}
else
{
scanf("%f",&t1);
C=t1*2;
if(C<l||C>r)
{
printf("0 0\n");
continue;
}
ans1=ans2=0;
query(l,r,1,C);
printf("%d %d\n",ans1,ans2);
}
}
}
}