UVALive 4730 - Kingdom(并查集+线段树)
题目链接 https://cn.vjudge.net/problem/UVALive-4730
【题意】
平面上有n个城市,初始时城市之间没有任何双向道路相连,你的任务是依次执行以下指令
road A B 在城市A,B之间建立一条双向道路,保证这条道路不和其它道路在非端点处相交
line C 询问一条y=C的水平线和多少个州相交,以及这些州一共包含多少个城市,在任意时刻,每一组连通的城市形成一个州,保证C的小数部分为0.5
【输入格式】
多组输入,第一行为数据组数T. 每组数据第一行为一个整数n(1<=n<=1e5)代表城市数量,以下n行每行两个整数x,y(0 <= x,y <= 1e6)城市编号为0~n-1. 下一行为指令总数m(1<=m<=200000)以下m行每行一条指令,其中A,B为不同的整数且0<=A< B< N, C是小数部分保证为0.5的实数,且0 < C <1000000. 不会有两条道路连接相同的城市,每组数据至少有一条line指令
【输出格式】
对于每条line指令,输出y=C穿过的州的数目以及这些州包含的城市总数
【思路】
把每个城市抽象成点,题目中的州其实就是一个连通块,我们可以用并查集来维护一个连通块的信息,我们需要维护一个连通块中城市的数目,还有就是纵坐标的最大值和最小值以便进行合并。而合并操作又可以借助于线段树来完成,首先可以将坐标统一乘以2使得坐标值均为整数,之后对y轴上的每一个点抽象成线段树的一个叶子结点,线段树叶子结点维护2个信息,即这一点的坐标穿过的州的数量和对应城市的数量(非叶结点这两个值是无意义的)合并两个连通块的过程其实是把原来的两个连通块删掉,加入合并后的新连通块。比如在合并两个连通块A和B的时候,首先把A,B原来各自的区间对应的州的数量减1,城市数量减去自己的城市数量,然后在合并后的新的区间中使得州的数量加1,城市数量加上A,B城市的总数量.可以用lazy标记维护区间和来实现区间更新,最后单点查询即可因为只有叶子结点的信息有意义.
#include<bits/stdc++.h>
#define node tree[id]
#define lson tree[id<<1]
#define rson tree[id<<1|1]
using namespace std;
const int maxn=1000005;
const int maxm=2000050;
struct Tree{
int left,right;
int num,city;
int lazy1,lazy2;
};
int n,m;
int par[maxn];
int maxy[maxn],miny[maxn],cnt[maxn];
int x[maxn],y[maxn];
Tree tree[maxm<<2];
void init(){
for(int i=0;i<=n;++i) par[i]=i;
memset(maxy,0,sizeof(maxy));
memset(miny,0,sizeof(miny));
memset(cnt,0,sizeof(cnt));
}
int find(int x){ return x==par[x]?x:par[x]=find(par[x]); }
void pushup(int id){
node.num=lson.num+rson.num;
node.city=lson.city+rson.city;
}
void pushdown(int id){
if(node.lazy1 && node.left!=node.right){
lson.lazy1+=node.lazy1;
lson.num+=(lson.right-lson.left+1)*node.lazy1;
rson.lazy1+=node.lazy1;
rson.num+=(rson.right-rson.left+1)*node.lazy1;
node.lazy1=0;
}
if(node.lazy2 && node.left!=node.right){
lson.lazy2+=node.lazy2;
lson.city+=(lson.right-lson.left+1)*node.lazy2;
rson.lazy2+=node.lazy2;
rson.city+=(rson.right-rson.left+1)*node.lazy2;
node.lazy2=0;
}
}
void build(int id,int le,int ri){
node.left=le;
node.right=ri;
node.num=node.city=0;
node.lazy1=node.lazy2=0;
if(le==ri) return;
int mid=(le+ri)>>1;
build(id<<1,le,mid);
build(id<<1|1,mid+1,ri);
}
void update(int id,int le,int ri,int a,int b){
if(node.left==le && node.right==ri){
node.lazy1+=a;
node.num+=(node.right-node.left+1)*a;
node.lazy2+=b;
node.city+=(node.right-node.left+1)*b;
return;
}
pushdown(id);
int mid=(node.left+node.right)>>1;
if(ri<=mid) update(id<<1,le,ri,a,b);
else if(le>mid) update(id<<1|1,le,ri,a,b);
else{
update(id<<1,le,mid,a,b);
update(id<<1|1,mid+1,ri,a,b);
}
pushup(id);
}
int query(int id,int pos){
if(node.left==node.right) return id;
pushdown(id);
int mid=(node.left+node.right)>>1;
if(pos<=mid) return query(id<<1,pos);
else return query(id<<1|1,pos);
}
int main(){
//freopen("in.txt","r",stdin);
//freopen("out2.txt","w",stdout);
int T;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
init();
int maxr=0;
for(int i=0;i<n;++i){
scanf("%d%d",&x[i],&y[i]);
y[i]<<=1;
maxr=max(maxr,y[i]);
maxy[i]=miny[i]=y[i];
cnt[i]=1;
}
build(1,0,maxr);
for(int i=0;i<n;++i){ update(1,y[i],y[i],1,1); }
scanf("%d",&m);
while(m--){
char op[10];
scanf("%s",op);
if(op[0]=='r'){
int a,b;
scanf("%d%d",&a,&b);
a=find(a);
b=find(b);
if(a!=b){
update(1,miny[a],maxy[a],-1,-cnt[a]);
update(1,miny[b],maxy[b],-1,-cnt[b]);
par[b]=a;
maxy[a]=max(maxy[a],maxy[b]);
miny[a]=min(miny[a],miny[b]);
cnt[a]+=cnt[b];
update(1,miny[a],maxy[a],1,cnt[a]);
}
}
else{
double c;
scanf("%lf",&c);
int p=c*2+0.5;
if(p>maxr) puts("0 0");
else{
int id=query(1,p);
printf("%d %d\n",node.num,node.city);
}
}
}
}
return 0;
}