启发式合并
例题: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;
}
愿你在冷铁卷刃之前,得以窥见天光

浙公网安备 33010602011771号