题意
有 n 个点和
m 个操作,分为两种:
①. road uv 表示连接 u 号点和v 号点
②. line C (C 为小数,并且小数部分一定为0.5)表示询问 y=C 这条直线穿越了几个联通块,这些联通块的点数之和是多少
n≤105,m≤2∗105
解法
并查集+线段树:
一开始错的很惨啊……不过最后还是自己改出来了……
运行速度还是挺快的,在这里排 Rank3
对于连边操作,应该想到并查集,同时维护每一个并查集的 siz ,这个很容易实现,难点是②操作:
因为询问比较多,所以单次询问的复杂度应该在 logn 级别,大家应该都会想到线段树
对于一个联通块 S ,考虑再维护两个信息:下边界和上边界(y 轴方向上),在合并两个并查集的时候,同时更新这两个信息:
然后我们就可以将每一个联通块对应在线段树上的一个区间,这样我们就可以利用线段树做到 log 的查询,下面考虑怎么维护区间信息:
假设目前要合并两个联通块 S 和U ,那么应该首先去除掉这两个联通块原本的贡献,然后合并之后加上新联通块的贡献,这样就不会有重复
所以我们在线段树中记录区间左右端点,完全覆盖了这一个区间的联通块个数以及这些联通块中的点数之和(完全覆盖指这一个区间是某个联通块的覆盖范围的子集)
在进行区间操作时,因为覆盖的区间长度可能比较大,所以肯定不能够暴力覆盖所有的单点,而是要打 lazy 标记。不过本题的区间维护很特殊,并不能够通过子节点来更新父节点,而只能够通过父节点更新子节点:因为如果某个联通块完全覆盖了某个区间 u ,但是并不一定完全覆盖了u 的父节点,相反, u 的子节点一定会被完全覆盖
然后还有一个问题就是C 是一个小数,这让我们很恼火。不过可以将每一个点的纵坐标都乘以2,然后 C 也乘以2,这样的话就可以把所有的点变为整数,不过相应的,空间也要开两倍大(我就是这里被坑了好久……数组开小了可能是RE ,可能会 WA ,也有可能导致 TLE )
复杂度
O( T∗mlogn )
代码
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#define ls 2*k
#define rs 2*k+1
#define Rint register int
#define Lint long long int
using namespace std;
const int N=2000100;
int up[N],down[N];
int f[N],siz[N];
int T,n,m;
struct Segment_tree
{
struct node
{
int l,r;
int cnt,siz;
}t[N*4];
struct task
{
int cnt,siz;
}A;
void build(Rint k,Rint l,Rint r)
{
t[k].l=l,t[k].r=r;
if( l==r ) return ;
int mid=(l+r)/2;
build( ls,l,mid ),build( rs,mid+1,r );
}
void pushdown(Rint k)
{
if( ls>N*4 ) return ;
int &x=t[k].cnt,&y=t[k].siz;
t[ls].cnt+=x,t[rs].cnt+=x;
t[ls].siz+=y,t[rs].siz+=y;
x=y=0;
}
void insert(Rint k,Rint l,Rint r,Rint x,Rint w)
{
if( ( k==1 && l==r ) || l>r ) return ;
if( t[k].l==l && t[k].r==r )
{
t[k].cnt+=w,t[k].siz+=w*siz[x];
return ;
}
pushdown( k );
if( r<=t[ls].r ) insert( ls,l,r,x,w );
else
if( l>=t[rs].l ) insert( rs,l,r,x,w );
else insert( ls,l,t[ls].r,x,w ),insert( rs,t[rs].l,r,x,w );
}
task query(Rint k,Rint x)
{
if( t[k].l==t[k].r ) return (task){ t[k].cnt,t[k].siz };
pushdown( k );
if( x<=t[ls].r ) return query( ls,x );
else return query( rs,x );
}
void clear(Rint k,Rint l,Rint r)
{
t[k].l=0,t[k].r=0;
t[k].cnt=t[k].siz=0;
if( l==r ) return ;
int mid=(l+r)/2;
clear( ls,l,mid ),clear( rs,mid+1,r );
}
}Tr;
int find(int x)
{
if( x!=f[x] ) f[x]=find( f[x] );
return f[x];
}
void Union(int u,int v)
{
f[u]=v;
siz[v]+=siz[u];
up[v]=max( up[v],up[u] );
down[v]=min( down[v],down[u] );
}
int main()
{
char s[10];
double x;
int u,v,mx;
scanf("%d",&T);
while( T-- )
{
scanf("%d",&n);
mx=0;
for(int i=1;i<=n;i++) f[i]=i,siz[i]=1;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&u,&v);
up[i]=down[i]=v*2;
mx=max( mx,v );
}
mx=mx*2;
Tr.build( 1,1,mx );
scanf("%d",&m);
while( m-- )
{
scanf("%s",s);
if( s[0]=='r' )
{
scanf("%d%d",&u,&v);
u++,v++;
u=find( u ),v=find( v );
if( u!=v )
{
Tr.insert( 1,down[u]+1,up[u],u,-1 );
Tr.insert( 1,down[v]+1,up[v],v,-1 );
Union( u,v );
Tr.insert( 1,down[v]+1,up[v],v,1 );
}
}
else
{
scanf("%lf",&x);
v=x*2;
if( v>mx ) { printf("0 0\n");continue ; }
Tr.A=Tr.query( 1,v );
printf("%d %d\n",Tr.A.cnt,Tr.A.siz);
}
}
Tr.clear( 1,1,mx );
}
return 0;
}