启发式合并

例题:bzoj4919大根堆

  洛谷P4577领导集团问题

 

以大根堆为例

就是说我在将它和它的子树合并的时候
如果他的子树里有比它大的,那么我直接将
大于等于它的第一个数换进去
这样能保证在序列长度不变的情况下使最大值最小,如果它子树里没有比他大的,直接插入就行
eg : 1 4 7 8 9
我插入6,那么就会把7换掉,这样,序列长度还是5不会变,但是如果单独拿出来看146肯定是比147好的(启发式合并,将小的向大的合并,所以将最大的根越小越好->更容易合并进大的)但如果用146合并出来的比序列长为5的短,那么对答案不会有影响
但如果比5长,那么以6插入肯定比7好

大根堆

点击查看代码
#include <bits/stdc++.h>
#define LL long long 
#define Re register int
#define ki cout << endl 
#define LD double
using namespace std;

namespace kiritokazuto {
	template <typename T> inline void in(T &x) {
		int f = 0; x = 0; char c = getchar();
		while(c < '0' || c > '9')f |= c == '-', c = getchar();
		while(c >= '0' && c <= '9')x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
		x = f ? -x : x;
	}
	
	template <typename T> inline void ot(T x) {
		if(x < 0)putchar('-'), x = -x;
		if(x > 9)ot(x / 10); putchar(x % 10 | '0'); 
	}
	
}
const int maxn = 2e5 + 100;
const int Inf = 1e9;

using namespace kiritokazuto;
//就是想试试 这个 “启发式合并 ”
//在当前大根堆大小相同的时候,顶点的权值越小,就能合并给更大的大根堆
//启发式合并 -> 小的往大的里和 -> 所以不去考虑当前顶点的权值更大 -> 这样是将别的合并到当前 -> 就不是启发了(。。。好像倒过来也是?) 
/*
当前点与子树合并
如果子树内有比当前点更大的值
答案不会变
直接替换set中比它大与它相邻的树
否则直接加进来
*/
/*
set c
产生一个空的set/multiset,不含任何元素

set c(op)
以op为排序准则,产生一个空的set/multiset

//strcut cmp {
	bool operator() (const rec &a, const rec&b) {
		return a.x < b.x || a.x == b.x && a.y < b.y;
	}
} ;
  multiset<rec, cmp> mul;
//

set c1(c2)
产生某个set/multiset的副本,所有元素都被拷贝

set c(beg,end)
以区间[beg,end)内的所有元素产生一个set/multiset

set c(beg,end, op)
以op为排序准则,区间[beg,end)内的元素产生一个set/multiset

c.~set()
销毁所有元素,释放内存

set<Elem>
产生一个set,以(operator <)为排序准则

set<Elem,op>
产生一个set,以op为排序准则

count (elem)
返回元素值为elem的个数

find(elem)
返回元素值为elem的第一个元素,如果没有返回end()

lower _bound(elem)
返回元素值为elem的第一个可安插位置,也就是元素值 >= elem的第一个元素位置

upper _bound (elem)
返回元素值为elem的最后一个可安插位置,也就是元素值 > elem 的第一个元素位置

equal_range (elem)
返回elem可安插的第一个位置和最后一个位置,也就是元素值==elem的区间


c.size()
返回当前的元素数量

c.empty ()
判断大小是否为零,等同于0 == size(),效率更高

c.max_size()
返回能容纳的元素最大数量

c1 == c2
判断c1是否等于c2

c1 != c2
判断c1是否不等于c2(等同于!(c1==c2))

c1 < c2
判断c1是否小于c2

c1 > c2
判断c1是否大于c2

c1 <= c2
判断c1是否小于等于c2(等同于!(c2<c1))

c1 >= c2
判断c1是否大于等于c2 (等同于!(c1<c2))

c1 = c2
将c2的元素全部给c1

c1.swap(c2)
将c1和c2 的元素互换

swap(c1,c2)
同上,全局函数

c.begin()
返回一个随机存取迭代器,指向第一个元素

c.end()
返回一个随机存取迭代器,指向最后一个元素的下一个位置

c.rbegin()
返回一个逆向迭代器,指向逆向迭代的第一个元素

c.rend()
返回一个逆向迭代器,指向逆向迭代的最后一个元素的下一个位置

c.insert(elem)
插入一个elem副本,返回新元素位置,无论插入成功与否。

c.insert(pos, elem)
安插一个elem元素副本,返回新元素位置,pos为收索起点,提升插入速度。

c.insert(beg,end)
将区间[beg,end)所有的元素安插到c,无返回值。

c.erase(elem)
删除与elem相等的所有元素,返回被移除的元素个数。

c.erase(pos)
移除迭代器pos所指位置元素,无返回值。

c.erase(beg,end)
移除区间[beg,end)所有元素,无返回值。

c.clear()
移除所有元素,将容器清空
sets和multisets的迭代器是双向迭代器
对迭代器操作而言
所有的元素都被视为常数
可以确保你不会人为改变元素值
从而打乱既定顺序
所以无法调用变动性算法,如remove()。
必须保证参数有效
迭代器必须指向有效位置
序列起点不能位于终点之后
不能从空容器删除元素。
*/
#define mulit multiset <int> :: iterator
#define si(x) mul[x].size()
multiset <int> mul[maxn];//存最长序列的尾部 
vector <int> G[maxn];
int n, val[maxn];
void dfs(int u) {
	//如果当前子树没有更大的,直接加,就是当前的根节点严格大于尾部 
	//否则,当前跟节点小于等于尾部, 则替换大于等于它的第一个数(lower_bound),保证长度最长且权值最小 
	for(Re i = 0, to; i < G[u].size(); i ++) {
		to = G[u][i];
	//	if(to == fa)continue;
		dfs(to);
		if(si(to) > si(u))swap(mul[to], mul[u]); 
		for(mulit it = mul[to].begin(); it != mul[to].end(); it++) mul[u].insert(*it);//mulit默认从小到大 
		mul[to].clear();
	}
	mulit it = mul[u].lower_bound(val[u]);
	if(it != mul[u].end()) mul[u].erase(it);
	mul[u].insert(val[u]);
}

signed main () {
	in(n);
	for(Re i = 1, x; i <= n; i ++) {
		in(val[i]);
		in(x);
		//G[i].push_back(x);
		G[x].push_back(i);
	}
	dfs(1);
	ot(mul[1].size());
	return 0;
} 

领导集团问题

点击查看代码
#include <bits/stdc++.h>
#define LL long long 
#define Re register int
#define ki cout << endl 
#define LD double
using namespace std;

namespace kiritokazuto {
	template <typename T> inline void in(T &x) {
		int f = 0; x = 0; char c = getchar();
		while(c < '0' || c > '9')f |= c == '-', c = getchar();
		while(c >= '0' && c <= '9')x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
		x = f ? -x : x;
	}
	
	template <typename T> inline void ot(T x) {
		if(x < 0)putchar('-'), x = -x;
		if(x > 9)ot(x / 10); putchar(x % 10 | '0'); 
	}
	
}
const int maxn = 2e5 + 100;
const int Inf = 1e9;

using namespace kiritokazuto;

#define mulit multiset <int> :: iterator
#define si(x) mul[x].size()

multiset <int> mul[maxn];//存最长序列的尾部 
vector <int> G[maxn];
int n, val[maxn];


void dfs(int u) {
	for(Re i = 0, to; i < G[u].size(); i ++) {
		to = G[u][i];
		dfs(to);
		if(si(to) > si(u))swap(mul[to], mul[u]); 
		for(mulit it = mul[to].begin(); it != mul[to].end(); it++) mul[u].insert(*it);//mulit默认从小到大 
	   // mul[to].clear();
	}
	mul[u].insert(val[u]);//先插入,保证有...
	mulit it = mul[u].lower_bound(val[u]);
	if(it != mul[u].begin()) mul[u].erase(--it);//这次该改begin
	
}

signed main () {
  //  freopen("init.in.txt", "r", stdin);
   // freopen("outt.out.txt", "w", stdout);
	in(n);
	for(Re i = 1, x; i <= n; i ++) {
		in(val[i]);
	}
	for(Re i = 2, x; i <= n; i ++) {
		in(x);
		G[x].push_back(i);
	}
	dfs(1);
	ot(mul[1].size());
   // cout << "??";
	return 0;
} 
posted @ 2022-04-09 20:07  kiritokazuto  阅读(71)  评论(0)    收藏  举报