zoukankan      html  css  js  c++  java
  • [HNOI2012]永无乡

    题意

    永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛。如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的。现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥。Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪 座,请你输出那个岛的编号。

    对于 100%的数据 n≤100000,m≤n,q≤300000

    分析

    参照hzwer的题解。

    此题直接线段树合并水过。

    只要用并查集维护连通性,每个连通块内建一棵权值线段树,操作只有合并俩线段树以及查询排名。

    时间复杂度(O(n log n))

    如今平衡树的问题大多可以用权值线段树来做,那么何乐而不为呢?尤其是这种带合并的题。网上说可以Splay启发式合并,听起来高大上,实际也没什么东西。况且权值线段树常数这么优秀,合并也是异常方便。

    代码

    #include<bits/stdc++.h>
    #define rg register
    #define il inline
    #define co const
    template<class T>il T read()
    {
    	rg T data=0;
    	rg int w=1;
    	rg char ch=getchar();
    	while(!isdigit(ch))
    	{
    		if(ch=='-')
    			w=-1;
    		ch=getchar();
    	}
    	while(isdigit(ch))
    	{
    		data=data*10+ch-'0';
    		ch=getchar();
    	}
    	return data*w;
    }
    template<class T>il T read(rg T&x)
    {
    	return x=read<T>();
    }
    using namespace std;
    typedef long long ll;
    
    co int N=1e5+7,LG=18;
    int v[N],id[N];
    
    namespace DS
    {
    	int fa[N];
    	
    	int find(int x)
    	{
    		return fa[x]==x?x:fa[x]=find(fa[x]);
    	}
    }
    
    int root[N],tot;
    namespace T
    {
    	int ls[N*LG],rs[N*LG],sum[N*LG];
    	
    	void insert(int&x,int l,int r,int val)
    	{
    		if(!x)
    			x=++tot;
    		if(l==r)
    		{
    			sum[x]=1;
    			return;
    		}
    		int mid=(l+r)/2;
    		if(val<=mid)
    			insert(ls[x],l,mid,val);
    		else
    			insert(rs[x],mid+1,r,val);
    		sum[x]=sum[ls[x]]+sum[rs[x]];
    	}
    	
    	int query(int x,int l,int r,int rank)
    	{
    		if(l==r)
    			return l;
    		int mid=(l+r)/2;
    		if(sum[ls[x]]>=rank)
    			return query(ls[x],l,mid,rank);
    		else
    			return query(rs[x],mid+1,r,rank-sum[ls[x]]);
    	}
    	
    	int merge(int x,int y)
    	{
    		if(!x||!y)
    			return x+y;
    		ls[x]=merge(ls[x],ls[y]);
    		rs[x]=merge(rs[x],rs[y]);
    		sum[x]=sum[ls[x]]+sum[rs[x]];
    		return x;
    	}
    }
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	int n,m;
    	read(n),read(m);
    	for(int i=1;i<=n;++i)
    	{
    		read(v[i]);
    		id[v[i]]=i;
    		DS::fa[i]=i;
    	}
    	for(int i=1;i<=m;++i)
    	{
    		int x,y;
    		read(x),read(y);
    		int p=DS::find(x),q=DS::find(y);
    		DS::fa[p]=q;
    	}
    	for(int i=1;i<=n;++i)
    		T::insert(root[DS::find(i)],1,n,v[i]);
    	int k;
    	read(k);
    	while(k--)
    	{
    		char ch[2];
    		scanf("%s",ch);
    		int x,y;
    		read(x),read(y);
    		if(ch[0]=='Q')
    		{
    			int p=DS::find(x);
    			if(T::sum[root[p]]<y)
    			{
    				puts("-1");
    				continue;
    			}
    			int t=T::query(root[p],1,n,y);
    			printf("%d
    ",id[t]);
    		}
    		else
    		{
    			int p=DS::find(x),q=DS::find(y);
    			if(p!=q)
    			{
    				DS::fa[p]=q;
    				root[q]=T::merge(root[p],root[q]);
    			}
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    English 2
    速算24点
    心理学1
    从微服务到函数式编程
    034 01 Android 零基础入门 01 Java基础语法 04 Java流程控制之选择结构 01 流程控制概述
    033 01 Android 零基础入门 01 Java基础语法 03 Java运算符 13 运算符和表达式知识点总结
    032 01 Android 零基础入门 01 Java基础语法 03 Java运算符 12 运算符和if-else条件语句的综合案例——闰年问题
    031 01 Android 零基础入门 01 Java基础语法 03 Java运算符 11 运算符的优先级
    030 01 Android 零基础入门 01 Java基础语法 03 Java运算符 10 条件运算符
    029 01 Android 零基础入门 01 Java基础语法 03 Java运算符 09 逻辑“非”运算符
  • 原文地址:https://www.cnblogs.com/autoint/p/10266434.html
Copyright © 2011-2022 走看看