POJ 2985 The k-th Largest Group(树状数组 并查集/查找第k大的数)

传送门

The k-th Largest Group

Time Limit: 2000MS   Memory Limit: 131072K
Total Submissions: 8690   Accepted: 2847

Description

Newman likes playing with cats. He possesses lots of cats in his home. Because the number of cats is really huge, Newman wants to group some of the cats. To do that, he first offers a number to each of the cat (1, 2, 3, …, n). Then he occasionally combines the group cat i is in and the group cat j is in, thus creating a new group. On top of that, Newman wants to know the size of the k-th biggest group at any time. So, being a friend of Newman, can you help him?

Input

  • 1st line: Two numbers N and M (1 ≤ NM ≤ 200,000), namely the number of cats and the number of operations.
  • 2nd to (m + 1)-th line: In each line, there is number C specifying the kind of operation Newman wants to do. If C = 0, then there are two numbers i and j (1 ≤ ij ≤ n) following indicating Newman wants to combine the group containing the two cats (in case these two cats are in the same group, just do nothing); If C = 1, then there is only one number k (1 ≤ k ≤ the current number of groups) following indicating Newman wants to know the size of the k-th largest group.

Output

For every operation “1” in the input, output one number per line, specifying the size of the kth largest group.

Sample Input

10 10

0 1 2

1 4

0 3 4

1 2

0 5 6

1 1

0 7 8

1 1

0 9 10

1 1

Sample Output

1

2

2

2

2

Hint

When there are three numbers 2 and 2 and 1, the 2nd largest number is 2 and the 3rd largest number is 1.

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 200005;
int N,root[maxn],a[maxn],c[maxn];

int find(int x)
{
	if (root[x] != x)	root[x] = find(root[x]);
	return root[x];
} 

int find_kth(int x) //查找第k小的元素
{
	int ans = 0,cnt = 0;
	for (int i = 20;i >= 0;i--)   //这里的20适当的取值,与MAX_VAL有关,一般取lg(MAX_VAL)
	{
		ans += (1 << i);
		if (ans > N || cnt + c[ans] >= x)
			ans -= (1 << i);
		else
			cnt += c[ans];
	}
	return ans + 1;
}

void upd(int i,int v)
{
	while (i <= N)
	{
		c[i] += v;
		i += i & -i;
	}
}

int main()
{
	int M;
	while (~scanf("%d%d",&N,&M))
	{
		int tot = N;
		memset(c,0,sizeof(c));
		for (int i = 1;i <= N;i++)	root[i] = i,a[i] = 1; //a[i]表示编号为i的Group大小
		upd(1,N);                //初始Group大小为1的有N个
		while (M--)
		{
			int opt,x,y;
			scanf("%d",&opt);
			if (opt == 0)
			{
				scanf("%d%d",&x,&y);
				x = find(x);
				y = find(y);
				if (x == y)	continue;
				upd(a[x],-1);
				upd(a[y],-1);       //x与y合并,则编号为x的Group与编号为y的Group大小减1
				upd(a[x] + a[y],1); //大小为a[x]+a[y]的Group的大小增1
				root[y] = x;        //y的祖先节点为x;
				a[x] += a[y];       //编号为x的Group大小增加a[y]
				tot--;              //并查集合并,则总元素减少1
			}
			else
			{
				scanf("%d",&x);
				printf("%d\n",find_kth(x));
			}
		}
	}
	return 0;
}

  

时间: 2024-10-20 21:42:44

POJ 2985 The k-th Largest Group(树状数组 并查集/查找第k大的数)的相关文章

Wikioi 2492 树状数组+并查集(单点更新区间查询)

刚开始做的时候用线段树做的,然后就跳进坑里了--因为要开方,所以区间的值都得全部变,然后想用lazy标记的,但是发现用不了,单点更新这个用不了,然后就不用了,就T了.然后实在不行了,看了别人的题解,原来是用树状数组+并查集的方法,唉--没想到啊! 因为开方之后多次那个数就会变成1了,所以是1的时候开方下去就没用了.树状数组更新的时候就把其更新的差更新即可,太机智了这题-- 昨天做了,然后出错找了好久都找不出来,原来是把s[i]写成c[i]了,然后答案一直错,晕-- #include <iostr

BZOJ 3211 花神游历各国 (树状数组+并查集)

题解:首先,单点修改求区间和可以用树状数组实现,因为开平方很耗时间,所以在这个方面可以优化,我们知道,开平方开几次之后数字就会等于1 ,所以,用数组记录下一个应该开的数,每次直接跳到下一个不是1的数字进行开平方,至于这个数组,可以用并查集维护. #include <cstdio> #include <cmath> #include <iostream> using namespace std; typedef long long LL; LL c[100005]; in

hdu 2985 The k-th Largest Group 树状数组求第K大

The k-th Largest Group Time Limit: 2000MS   Memory Limit: 131072K Total Submissions: 8353   Accepted: 2712 Description Newman likes playing with cats. He possesses lots of cats in his home. Because the number of cats is really huge, Newman wants to g

POJ2985 The k-th Largest Group[树状数组求第k大值 并查集]

The k-th Largest Group Time Limit: 2000MS   Memory Limit: 131072K Total Submissions: 8807   Accepted: 2875 Description Newman likes playing with cats. He possesses lots of cats in his home. Because the number of cats is really huge, Newman wants to g

CodeVS2492 上帝造题的七分钟2(树状数组+并查集)

传送门 树状数组模板题.注意优化,假设某个数的值已经是1了的话.那么我们以后就不用对他进行操作了,这个能够用并查集实现. 这道题还有个坑的地方,给出查询区间端点的a,b,有可能a>b. #include<cstdio> #include<cmath> #include<cctype> #include<iostream> using namespace std; inline void GET(int &t){ char c; t=0; do{

BZOJ 3211 花神游历各国 树状数组+并查集

题目大意:花神对每一个国家有一个喜爱程度,有的时候他会对连续的一段国家进行访问,求他的喜爱程度的和:有的时候他会对连续的一段国家产生厌恶,喜爱程度变成sqrt(x)下取整. 思路:乍一看好像是RMQ问题,用线段树就可以水过,但是开根号的标记怎么下传?这是一个严重的问题,所以我们要换一个思路. 注意到开根号有一个有趣的性质:sqrt(1) = 1,sqrt(0) = 0,而且所有的数字经过有限次的开根号运算都会变成1.这个性质就很好了.我们对每一个点暴力开根号,然后当这个店的点权变成1的时候就打一

[BZOJ3211]花神游历各国&amp;&amp;[BZOJ3038] 上帝造题的七分钟2 树状数组+并查集

3211: 花神游历各国 Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 4057  Solved: 1480[Submit][Status][Discuss] Description Input Output 每次x=1时,每行一个整数,表示这次旅行的开心度 Sample Input 4 1 100 5 5 5 1 1 2 2 1 2 1 1 2 2 2 3 1 1 4 Sample Output 101 11 11 HINT 对于100%的数据,

花神游历各国 题解(小清新线段树/树状数组+并查集)

众所周知,这是一道小清新线段树 然而可以用树状数组水过去且跑得飞快 看到区间开方第一反应肯定是线段树懒标记区间修改之类的,但是这个东西似乎确凿不可维护 所以考虑暴力循环单点修改->T飞 于是我们关注一下开方本身的特殊性 我们知道,如果每次向下取整,一个数经过多次操作最终会变成1(或0) 事实上,大概经过 log(logx)次就会变成1 这是什么概念呢?经过博主测试,1e9只要经过五次开方取整就会变成1 那么接下来就能够利用1每次不必再操作优化复杂度 可以维护一个类似链表的结构,指向下一个>1的

【BZOJ3211】花神游历各国 树状数组 并查集 均摊分析

链接: #include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/44686727"); } 题解: 一个点开几次方就没啦.所以我们只需要修改不是0或者1的点就行了. 均摊基本O(n). 然后用并查集维护一个点右边第一个不是0的数. 手写读入果然高大上.卡rank神器. 顺便Orz一下wys大神. 代