当前位置: 代码迷 >> 综合 >> 【UVA1455】Kingdom
  详细解决方案

【UVA1455】Kingdom

热度:83   发布时间:2023-12-05 12:35:36.0

题意

??有n个点和 m 个操作,分为两种:
??①.road?u? v 表示连接u号点和 v 号点
??②.line?C C 为小数,并且小数部分一定为0.5)表示询问y=C这条直线穿越了几个联通块,这些联通块的点数之和是多少
??n105m2?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 );elseif( 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;
}