您的位置:首页 > 其它

【UVA1455】Kingdom

2017-10-20 19:33 381 查看
题意

  有n个点和m个操作,分为两种:

  ①.road u v表示连接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
,down
;
int f
,siz
;
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: