set 容器详解 附大根堆题解

set · 浏览次数 : 11

小编点评

**PART 1 set什么是 set?** * set 是一个可以自动按升序排列的特殊容器。 * 它包含一些数据类型,也可以是字符类。 * 它的排序标准为字典序(强者处处是惊喜)。 **PART 2 大根堆题面思路常规办法为线段树合并,但太长了不想写 但总感觉有更优的做法,所以就有了下面基于 set 优化的 dfs 解题。** **代码:** ```c++ #include<bits/stdc++.h> inline int qr(){\tchar ch=getchar();int x=0,f=1;\tfor(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;\tfor(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);\treturn x*f;}#define qr qr()using namespace std;const int Ratio=0;const int N=500005;int n,cnt;int va[N],hh[N],to[N],ne[N];multiset<int>f[N];namespace Wisadel{\tvoid Wadd(int u,int v)\t{//加边 \t\tto[++cnt]=v;\t\tne[cnt]=hh[u];\t\thh[u]=cnt;\t}\tvoid Wdfs(int u,int fa)\t{\t\tfor(int i=hh[u];i!=-1;i=ne[i])\t\t{//遍历 \t\t\tint t=to[i];\t\t\tif(t==fa)\t\t\tcontinue;\t\t\tWdfs(t,u);\t\t\tif(f[u].size()<f[t].size())\t\t\t\tswap(f[u],f[t]);\t\t\t//大根堆,所以父节点的子树和应大于子节点 \t\t\tfor(auto j:f[t])\t\t\t\tf[u].insert(j);\t\t\t//类似线段树合并 将两棵树并到一起 \t\t\tf[t].clear();\t\t\t//擦去被合并了的树 \t\t}\t\tif(f[u].size()>0&&f[u].lower_bound(va[u])!=f[u].end())\t\t\tf[u].erase(f[u].lower_bound(va[u]));\t\t//因为是大根堆,所以子应小于父\t\t//那么若存在比父大的元素,这个堆便不成立\t\t//擦去它 \t\tf[u].insert(va[u]);\t\t//把当前结点(根节点)插入 \t}\tshort main()\t{\t\tmemset(hh,-1,sizeof hh);\t\tn=qr;\t\tfor(int i=1;i<=n;i++)\t\t{\t\t\tva[i]=qr;int b=qr;\t\t\tif(i!=1)\t\t\t\tWadd(i,b),Wadd(b,i);\t\t}\t\tWdfs(1,-1);\t\tprintf(\"%d\\",(int)f[1].size());\t\t//我们所用dfs的遍历形式,保证了会先将尽头的子树遍历尽\t\t//所以每个结点遍历后的结果,是包含了它以及它子树的最优解 \t\t//所以经过交换,最后答案会体现在f[1]容器内元素的个数 \t\treturn Ratio;\t}}int main(){return Wisadel::main();}完结撒花~。

正文

声明

本文中题解部分内容大部分转载自 @sonnety这篇博客中,本文为为方便复习而写的结论类文章,读者可自行跳转至原文处阅读。
image


PART 1 set

什么是 set

image
——来源cppreference

简言之,它就是一种存进去就可以自动按升序排列的特殊容器,通常的 set 还具有自动去重的功能。

定义方式:

std::set<int>s;
'set'+<存储数据类型>+容器名

注意的是,这里存储数据类型不仅包含常用的intlong longdouble等数值类,也可以是charstring这些字符类的,排序标准为字典序(强者处处是惊喜)

怎么使用 set

首先,我们要了解几个 set 的常用函数:

指针类
s.begin()//指向 s 的起始
s.end()//指向 s 的末尾
容量类
s.empty()//检查容器是否为空
s.size()//返回元素数(返回值类型为 unsigned int )
修改类
s.insert(w)//向容器中插入 w 这个元素
s.erase(w)//在容器中删除 w 这个元素
s.clear()//将 s 清空
swap(s1,s2)//交换 s1 与 s2 两个容器内的元素
查找类
s.find(w)//在容器中查找 w 这个元素所在的**地址**
         //若不存在该元素,则返回 s.end()
s.lower_bound(w)//在容器中寻找第一个不小于 w 的元素**地址**
                //若不存在则返回 s.end()

我们可以观察到,很多 set 的返回值类型都是特殊的,因此还存在一个迭代器set<int>::iterator,它是一个指向 set<int> 中元素的指针,可以通过迭代器访问 set<int> 中的元素,并能够进行迭代器运算,如自增等操作。

我们在调用 s 中的值的时候,可以如下操作:

输出 s 中的所有值
set<int>::iterator it;
for(it=s.begin();it!=s.end();it++)
    cout<<*it<<' ';// *it 是获取当前迭代器指向的元素的值

自 c++11 引入了类型 auto 后,我们可以更加简便地完成上面的操作。

for(auto it:s)
	cout<<it<<' ';//这里 auto 的类型等同于我们定义 s 时的数据类型,也就是 int 

set 和 multiset

multiset 在 cppreference 中定义如下:

image

简言之, multiset 的特性有:

  1. 可以保留重复的元素,也就是没有自动去重的性质。
  2. multiset 支持插入、删除和查找操作的平均时间复杂度均为 \(O(log\,n)\),而 set 只支持插入和查找操作的平均时间复杂度为 \(O(log\,n)\),删除操作的平均时间复杂度为 \(O(1)\)
  3. multiset 在删除时只会删除元素值相同的元素中的一个,而不是全部删除或删除一些。

通常情况下,我们多使用 set ,因为它在进行查找等操作时更快;而只有在需要保留重复元素的少数情况下,我们使用 multiset ,下题就是一个例子。

PART 2 大根堆

题面

image

思路

常规办法为线段树合并,但太长了不想写 但总感觉有更优的做法,所以就有了下面基于 set 优化的 dfs 做法。

我们可以通过 dp 引入,固定一点作为根结点,用f[u][i]表示以 \(u\) 为树根的子树里结点权值小于 \(i\) 的个数,其中 \(i\le v_u\);用 \(size_u\) 表示以 \(u\) 为根的树的大小。

那么很容易能想到状态转移方程为:

\[f[u][i]=\sum_{size_u}f[u][i],i\le v_u \]

\[f[u][i]=max(\sum_{size_u}f[u][i],\sum_{size_u}f[u][i+1]-1),i\ge v_u \]

那么如何存储? multiset !

set 自带的查找功能可以很方便的判断条件是否成立, \(size\) 函数也直接提供了上述 \(size\) 数组。

使用 dfs 进行遍历,由子结点逐个回溯至根节点,最后根节点的 \(size\) ,即为答案。

代码中加入了部分注释,供参考。

code:

#include<bits/stdc++.h>
inline int qr()
{
	char ch=getchar();int x=0,f=1;
	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
	for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);
	return x*f;
}
#define qr qr()
using namespace std;
const int Ratio=0;
const int N=500005;
int n,cnt;
int va[N],hh[N],to[N],ne[N];
multiset<int>f[N];
namespace Wisadel
{
	void Wadd(int u,int v)
	{//加边 
		to[++cnt]=v;
		ne[cnt]=hh[u];
		hh[u]=cnt;
	}
	void Wdfs(int u,int fa)
	{
		for(int i=hh[u];i!=-1;i=ne[i])
		{//遍历 
			int t=to[i];
			if(t==fa)
				continue;
			Wdfs(t,u);
			if(f[u].size()<f[t].size())
				swap(f[u],f[t]);
			//大根堆,所以父节点的子树和应大于子节点 
			for(auto j:f[t])
				f[u].insert(j);
			//类似线段树合并 将两棵树并到一起 
			f[t].clear();
			//擦去被合并了的树 
		}
		if(f[u].size()>0&&f[u].lower_bound(va[u])!=f[u].end())
			f[u].erase(f[u].lower_bound(va[u]));
		//因为是大根堆,所以子应小于父
		//那么若存在比父大的元素,这个堆便不成立
		//擦去它 
		f[u].insert(va[u]);
		//把当前结点(根节点)插入 
	}
	short main()
	{
		memset(hh,-1,sizeof hh);
		n=qr;
		for(int i=1;i<=n;i++)
		{
			va[i]=qr;int b=qr;
			if(i!=1)
				Wadd(i,b),Wadd(b,i);
		}
		Wdfs(1,-1);
		printf("%d\n",(int)f[1].size());
		//我们所用dfs的遍历形式,保证了会先将尽头的子树遍历尽
		//所以每个结点遍历后的结果,是包含了它以及它子树的最优解
		//所以经过交换,最后答案会体现在f[1]容器内元素的个数 
		return Ratio;
	}
}
int main(){return Wisadel::main();}

完结撒花~

image

与set 容器详解 附大根堆题解相似的内容:

set 容器详解 附大根堆题解

声明 本文中题解部分内容大部分转载自 @sonnety 的这篇博客中,本文为为方便复习而写的结论类文章,读者可自行跳转至原文处阅读。 PART 1 set 什么是 set ——来源cppreference 简言之,它就是一种存进去就可以自动按升序排列的特殊容器,通常的 set 还具有自动去重的功能。

5.1 C++ STL 集合数据容器

Set/Multiset 集合使用的是红黑树的平衡二叉检索树的数据结构,来组织泛化的元素数据,通常来说红黑树根节点每次只能衍生出两个子节点,左面的节点是小于根节点的数据集合,右面的节点是大于根节点的集合,通过这样的方式将数据组织成一颗看似像树一样的结构,而平衡一词的含义则是两边的子节点数量必须在小于等1的区间以内。Set集合天生去重,所有元素都会根据元素的键值自动的排序,并且Set元素在确定后无法

C++ STL 容器简单讲解

STL 简单讲解 网上有很多很好的资料可以参考 而直接看标准是最准确清晰的 vector stack queue / priority_queue deque array map / multimap set / multiset unordered_map unordered_set 关于指针和迭

Swift中常见的String用法,Array高阶使用,Set集合操作

String字符串常见用法 生成字符串 创建字符串 let greeting = "Hello, world!" let name = String("John") 连接字符串:使用加号(+)或者字符串插值(使用())来将多个字符串连接起来。 var firstName = "John" let l

[转帖]Working Set Size (WSS) Tools for Linux

https://github.com/brendangregg/wss These are experimental tools for doing working set size estimation, using different Linux facilities. See WARNINGs

Redis set数据类型命令使用及应用场景使用总结

转载请注明出处: 目录 1.sadd 集合添加元素 2.srem移除元素 3.smembers 获取key的所有元素 4.scard 获取key的个数 5.sismember 判断member元素是否存在集合key中 6.srandmember key count 从集合key中随机选出count个

Java 中的泛型 集合(List,Set) Map

泛型 集合(List,Set) Map 泛型 泛型的本质是参数化类型,即允许在编译时对集合进行类型检查,从而避免安全问题,提高代码的复用性 泛型的具体定义与作用 定义:泛型是一种在编译阶段进行类型检查的机制,它允许在类,方法,接口后通过<> 来声明类型参数.这些参数在编译时会被具体的类型替换.jav

哈希表(实现 Python 中的集合 set)

博客地址:https://www.cnblogs.com/zylyehuo/ # -*- coding: utf-8 -*- class LinkList: class Node: def __init__(self, item=None): self.item = item self.next =

[转帖]SQL Server JDBC – Set sendStringParametersAsUnicode to false

https://vladmihalcea.com/sql-server-jdbc-sendstringparametersasunicode/ https://learn.microsoft.com/en-us/sql/connect/jdbc/setting-the-connection-prop

【转帖】68.记忆集(remembered set)和写屏障(write barrier)

目录 1.记忆集(`remembered set`) 1.记忆集(remembered set) 问题:G1将堆区划分成多个region,一个region不可能是独立的,它其中存储的对象可能被其他任意region(这些region可能Old区或者Eden区)中的对象所引用。这样一来,在进行YGC的时