[学习笔记] 启发式合并
一、启发式合并
启发式合并多用于合并两个集合,现在有这样一个问题:
现在给定
如果我们暴力合并,时间复杂度会是
参考并查集的按秩合并,考虑将小的集合合并到大的集合上。
考虑计算时间复杂度,容易发现
二、洛谷 P3201 [HNOI2009] 梦幻布丁
https://www.luogu.com.cn/problem/P3201
考虑使用 std :: set
维护每个颜色的编号。
因为要进行启发式合并,所以我们要改变一些原有颜色表示的实际颜色,用
然后进行启发式合并,动态维护相邻两数颜色不同的即可,答案即为相邻颜色不同的数量加
时间复杂度是
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
typedef long long ll;
typedef pair < int, int > PII;
typedef int itn;
mt19937 RND_MAKER (chrono :: steady_clock :: now ().time_since_epoch ().count ());
inline ll randomly (const ll l, const ll r) {return (RND_MAKER () ^ (1ull << 63)) % (r - l + 1) + l;}
bool Memory_Begins;
const double pi = acos (-1);
//__gnu_pbds :: tree < Key, Mapped, Cmp_Fn = std :: less < Key >, Tag = rb_tree_tag, Node_Upadte = null_tree_node_update, Allocator = std :: allocator < char > > ;
//__gnu_pbds :: tree < PPS, __gnu_pbds :: null_type, less < PPS >, __gnu_pbds :: rb_tree_tag, __gnu_pbds :: tree_order_statistics_node_update > tr;
template < class Z >
inline void read (Z &tmp) {
Z x = 0, f = 0;
char c = getchar ();
for ( ; c < '0' | c > '9' ; c = getchar ()) f |= (c == '-');
for ( ; c >= '0' && c <= '9' ; c = getchar ()) x = (x << 1) + (x << 3) + (c & 15);
tmp = !f ? x : -x;
}
const int N = 1e5 + 5, M = 1e6 + 5;
int n, m, a[N], fa[M];
set < int > st[M];
set < int > id;
inline int found (int x) {
return fa[x] == x ? x : fa[x] = found (fa[x]);
}
bool Memory_Ends;
signed main () {
fprintf (stderr, "%.3lf MB\n", (&Memory_Begins - &Memory_Ends) / 1048576.0);
read (n), read (m);
for (int i = 0;i < M; ++ i) fa[i] = i;
for (int i = 1;i <= n; ++ i) {
read (a[i]);
st[a[i]].insert (i);
if (i > 1 && a[i] != a[i - 1]) id.insert (i - 1);
}
while (m --) {
int op, x, y;
read (op);
if (op == 1) {
read (x), read (y);
if (x == y) continue;
if (st[fa[x]].size () > st[fa[y]].size ()) swap (fa[x], fa[y]);
x = fa[x], y = fa[y];
for (int u : st[x]) {
if (id.count (u - 1)) id.erase (u - 1);
if (id.count (u)) id.erase (u);
}
for (int u : st[x]) st[y].insert (u);
for (int u : st[x]) {
if (!st[y].count (u - 1) && st[y].count (u) && u - 1 > 0) id.insert (u - 1);
if (!st[y].count (u + 1) && st[y].count (u) && u < n) id.insert (u);
}
st[x].clear ();
}
else {
printf ("%d\n", id.size () + 1);
}
}
fprintf (stderr, "%.3lf ms\n", 1e3 * clock () / CLOCKS_PER_SEC);
return 0;
}
/*
Things to check:
1. int ? long long ? unsigned int ? unsigned long long ?
2. array size ? is it enough ?
*/
三、P9168 [省选联考 2023] 人员调度 ( 部分分)
https://www.luogu.com.cn/problem/P9168
哎,后悔赛前不学启发式合并......
设可重集
显然
答案即为
用 std :: set
维护,可以做到
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
typedef long long ll;
typedef pair < int, int > PII;
typedef int itn;
mt19937 RND_MAKER (chrono :: steady_clock :: now ().time_since_epoch ().count ());
inline ll randomly (const ll l, const ll r) {return (RND_MAKER () ^ (1ull << 63)) % (r - l + 1) + l;}
bool Memory_Begins;
const double pi = acos (-1);
//__gnu_pbds :: tree < Key, Mapped, Cmp_Fn = std :: less < Key >, Tag = rb_tree_tag, Node_Upadte = null_tree_node_update, Allocator = std :: allocator < char > > ;
//__gnu_pbds :: tree < PPS, __gnu_pbds :: null_type, less < PPS >, __gnu_pbds :: rb_tree_tag, __gnu_pbds :: tree_order_statistics_node_update > tr;
template < class Z >
inline void read (Z &tmp) {
Z x = 0, f = 0;
char c = getchar ();
for ( ; c < '0' | c > '9' ; c = getchar ()) f |= (c == '-');
for ( ; c >= '0' && c <= '9' ; c = getchar ()) x = (x << 1) + (x << 3) + (c & 15);
tmp = !f ? x : -x;
}
const int N = 1e5 + 5;
int n, k, m, p[N], idx[N << 1], idv[N << 1], cnt, F[N], sub[N];
vector < int > G[N];
multiset < int > st[N];
multiset < int > lab[N];
inline void pre (int x) {
sub[x] = 1;
for (int v : G[x]) {
pre (v);
sub[x] += sub[v];
}
}
inline void dfs (int u) {
for (int v : G[u]) dfs (v);
for (int v : G[u]) {
if (lab[F[u]].size () >= lab[F[v]].size ()) {
for (int x : lab[F[v]]) lab[F[u]].insert (x);
}
else {
for (int x : lab[F[u]]) lab[F[v]].insert (x);
F[u] = F[v];
}
}
while (lab[F[u]].size () > sub[u]) {
int mn = *lab[F[u]].begin ();
lab[F[u]].erase (lab[F[u]].find (mn));
}
}
inline ll query () {
for (int i = 1;i <= n; ++ i) lab[i] = st[i], F[i] = i;
dfs (1);
ll ans = 0;
for (int w : lab[F[1]]) ans += 1ll * w;
return ans;
}
vector < ll > ans;
bool Memory_Ends;
signed main () {
// freopen ("transfer6.in", "r", stdin);
// freopen ("transfer6.out", "w", stdout);
// fprintf (stderr, "%.3lf MB\n", (&Memory_Begins - &Memory_Ends) / 1048576.0);
int sid; read (sid);
read (n), read (k), read (m);
for (int i = 1;i <= n; ++ i) G[i].clear ();
for (int i = 2;i <= n; ++ i) read (p[i]), G[p[i]].push_back (i);
pre (1);
for (int i = 1;i <= k; ++ i) {
int x, v; read (x), read (v);
st[x].insert (v);
idx[++ cnt] = x;
idv[cnt] = v;
}
ans.clear ();
ans.push_back (query ());
while (m --) {
int op; read (op);
if (op == 1) {
int x, v; read (x), read (v);
st[x].insert (v);
idx[++ cnt] = x;
idv[cnt] = v;
}
else {
int id; read (id);
int X = idx[id], V = idv[id];
st[X].erase (V);
}
ans.push_back (query ());
}
for (int i = 0;i < ans.size (); ++ i) {
if (i + 1 < ans.size ()) printf ("%lld ", ans[i]);
else printf ("%lld\n", ans[i]);
}
// fprintf (stderr, "%.3lf ms\n", 1e3 * clock () / CLOCKS_PER_SEC);
return 0;
}
/*
Things to check:
1. int ? long long ? unsigned int ? unsigned long long ?
2. array size ? is it enough ?
*/
四、P5290 [十二省联考 2019] 春节十二响
https://www.luogu.com.cn/problem/P5290
考虑解决以
设可重集
和 分别的最大值,次大值,第 大值,...,第 大值分别取较大的,与剩余的数组成可重集(删掉 和 分别的最大值,次大值,第 大值,...,第 大值)。
最好使用 std :: priority_queue
实现,当然 std :: set
也是可以的,时间复杂度
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
typedef long long ll;
typedef pair < int, int > PII;
typedef int itn;
mt19937 RND_MAKER (chrono :: steady_clock :: now ().time_since_epoch ().count ());
inline ll randomly (const ll l, const ll r) {return (RND_MAKER () ^ (1ull << 63)) % (r - l + 1) + l;}
bool Memory_Begins;
const double pi = acos (-1);
//__gnu_pbds :: tree < Key, Mapped, Cmp_Fn = std :: less < Key >, Tag = rb_tree_tag, Node_Upadte = null_tree_node_update, Allocator = std :: allocator < char > > ;
//__gnu_pbds :: tree < PPS, __gnu_pbds :: null_type, less < PPS >, __gnu_pbds :: rb_tree_tag, __gnu_pbds :: tree_order_statistics_node_update > tr;
template < class Z >
inline void read (Z &tmp) {
Z x = 0, f = 0;
char c = getchar ();
for ( ; c < '0' || c > '9' ; c = getchar ()) f |= (c == '-');
for ( ; c >= '0' && c <= '9' ; c = getchar ()) x = (x << 1) + (x << 3) + (c & 15);
tmp = !f ? x : -x;
}
const int N = 2e5 + 5;
int n, memo[N], fa[N];
vector < int > son[N];
priority_queue < int, vector < int >, less < int > > st[N];
inline void dfs (int u) {
for (int v : son[u]) dfs (v);
for (int v : son[u]) {
if (st[u].size () < st[v].size ()) st[u].swap (st[v]);
vector < int > oth; oth.clear ();
while (!st[v].empty ()) {
oth.push_back (max (st[u].top (), st[v].top ()));
st[u].pop (), st[v].pop ();
}
for (int x : oth) st[u].push (x);
}
st[u].push (memo[u]);
}
bool Memory_Ends;
signed main () {
fprintf (stderr, "%.3lf MB\n", (&Memory_Begins - &Memory_Ends) / 1048576.0);
read (n);
for (int i = 1;i <= n; ++ i) read (memo[i]);
for (int i = 2;i <= n; ++ i) read (fa[i]), son[fa[i]].push_back (i);
dfs (1);
ll ans = 0;
while (!st[1].empty ()) {
ans += 1ll * st[1].top ();
st[1].pop ();
}
printf ("%lld\n", ans);
fprintf (stderr, "%.3lf ms\n", 1e3 * clock () / CLOCKS_PER_SEC);
return 0;
}
/*
Things to check:
1. int ? long long ? unsigned int ? unsigned long long ?
2. array size ? is it enough ?
*/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端