• 【洛谷P3224】永无乡


    题目

    题目链接:https://www.luogu.com.cn/problem/P3224
    永无乡包含 \(n\) 座岛,编号从 \(1\)\(n\) ,每座岛都有自己的独一无二的重要度,按照重要度可以将这 \(n\) 座岛排名,名次用 \(1\)\(n\) 来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛到达另一个岛。如果从岛 \(a\) 出发经过若干座(含 \(0\) 座)桥可以 到达岛 \(b\) ,则称岛 \(a\) 和岛 \(b\) 是连通的。
    现在有两种操作:

    • \(B\ x\ y\) 表示在岛 \(x\) 与岛 \(y\) 之间修建一座新桥。
    • \(Q\ x\ k\) 表示询问当前与岛 \(x\) 连通的所有岛中第 \(k\) 重要的是哪座岛,即所有与岛 \(x\) 连通的岛中重要度排名第 \(k\) 小的岛是哪座,请你输出那个岛的编号。

    思路

    对于每一个点建立一棵权值线段树,区间\([l,r]\)维护在该点所连通的点中,有所少个排名在\([l,r]\)之间的岛。
    那么就十分显然了,对于\(B\)操作就线段树合并,\(Q\)操作就权值线段树查询第\(k\)小即可。
    注意如果并查集是\(x\)并到\(y\),那么线段树也要\(x\)并到\(y\)。当加入的一条边连接的两个点处于同一集合内就不用再合并了。
    时间复杂度\(O(q\log n)\)

    代码

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int N=100010;
    int n,m,father[N],rt[N],rk[N];
    char ch;
    
    int find(int x)
    {
    	return x==father[x]?x:father[x]=find(father[x]);
    }
    
    struct Treenode
    {
    	int lc,rc,cnt;
    };
    
    struct Tree
    {
    	Treenode tree[N*20];
    	int tot;
    	
    	void pushup(int x)
    	{
    		tree[x].cnt=tree[tree[x].lc].cnt+tree[tree[x].rc].cnt;
    	}
    	
    	void update(int &x,int l,int r,int k)
    	{
    		if (!x) x=++tot;
    		if (l==r)
    		{
    			tree[x].cnt=1;
    			return;
    		}
    		int mid=(l+r)>>1;
    		if (k<=mid) update(tree[x].lc,l,mid,k);
    			else update(tree[x].rc,mid+1,r,k);
    		pushup(x);
    	}
    	
    	void merge(int &x,int y)
    	{
    		if (!x || !y) x+=y;
    		else
    		{
    			merge(tree[x].lc,tree[y].lc);
    			merge(tree[x].rc,tree[y].rc);
    			pushup(x);
    		}
    	}
    	
    	int ask(int x,int l,int r,int k)
    	{
    		if (l==r)
    		{
    			if (k==1 && tree[x].cnt==1) return rk[l];
    				else return -1;
    		}
    		int mid=(l+r)>>1;
    		if (tree[tree[x].lc].cnt>=k)
    			return ask(tree[x].lc,l,mid,k);
    		else
    			return ask(tree[x].rc,mid+1,r,k-tree[tree[x].lc].cnt);
    	}
    }tree;
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for (int i=1,x;i<=n;i++)
    	{
    		scanf("%d",&x);
    		tree.update(rt[i],1,n,x);
    		rk[x]=father[i]=i;
    	}
    	for (int i=1,x,y;i<=m;i++)
    	{
    		scanf("%d%d",&x,&y);
    		x=find(x); y=find(y);
    		if (x!=y)
    		{
    			father[x]=y;
    			tree.merge(rt[y],rt[x]);
    		}
    	}
    	scanf("%d",&m);
    	while (m--)
    	{
    		while (ch=getchar()) if (ch=='B'||ch=='Q') break;
    		int x,y;
    		scanf("%d%d",&x,&y);
    		if (ch=='B')
    		{
    			x=find(x); y=find(y);
    			if (x!=y)
    			{
    				father[x]=y;
    				tree.merge(rt[y],rt[x]);
    			}
    		}
    		else
    		{
    			x=find(x);
    			printf("%d\n",tree.ask(rt[x],1,n,y));
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    lamp架构之升级php版本
    Linux常用命令大全
    Mysql表连接查询
    PHP练习题三
    PHP练习题二
    php 设计模式
    LAMP环境搭建教程
    Storm入门(四)WordCount示例
    Storm入门(三)HelloWorld示例
    Storm入门(一)原理介绍
  • 原文地址:https://www.cnblogs.com/stoorz/p/12243048.html
Copyright © 2020-2023  润新知