启发式合并
Posted jasony
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了启发式合并相关的知识,希望对你有一定的参考价值。
启发式合并
概念
启发式算法是基于人类的经验和直观感觉,对一些算法的优化。
作用
可以启发式合并更加高级的数据结构,如 (heap,~set,~splays) 等
复杂度计算
每次把个数少的合并到个数多的?复杂度 (O(min(m1,m2)))
可是我们注意到,每次合并后个数为合并前少的部分的个数的两倍以上,每个元素最多合并 (logm) 次,总复杂度 (O(mlogm)) 。
当合并 (heap,~set,~splays) 等,复杂度 (O(mlog2m))(作者太弱,不会证明)
例题1
[HNOI2009] 梦幻布丁
思路
对于每一个颜色,建一条链表。然后染色就是把链短的合并到链长的。
需要注意细节,如果把2染成3,但2的链比3的长,就需要把3的合并到2上。但是现在本应属于3的链在2上,就需要记一个该颜色的链现在在哪个颜色上,即是代码中的 (now) 数组。
代码
#include<cstdio>
#define rep(i, a, b) for (register int i=(a); i<=(b); ++i)
#define per(i, a, b) for (register int i=(a); i>=(b); --i)
using namespace std;
void swap(int &x, int &y){x^=y^=x^=y;}
const int N=1000005;
int head[N], nxt[N], col[N], now[N], cnt[N], st[N], ans;
inline int read()
{
int x=0,f=1;char ch=getchar();
for (;ch<‘0‘||ch>‘9‘;ch=getchar()) if (ch==‘-‘) f=-1;
for (;ch>=‘0‘&&ch<=‘9‘;ch=getchar()) x=(x<<1)+(x<<3)+ch-‘0‘;
return x*f;
}
void merge(int x, int y)
{
cnt[y]+=cnt[x]; cnt[x]=0;
for (int i=head[x]; i; i=nxt[i])
{
if (col[i+1]==y) ans--;
if (col[i-1]==y) ans--;
}
for (int i=head[x]; i; i=nxt[i]) col[i]=y;
nxt[st[x]]=head[y]; head[y]=head[x];
head[x]=st[x]=cnt[x]=0;
}
int main()
{
int n=read(), m=read();
rep(i, 1, n)
{
col[i]=read(); now[col[i]]=col[i];
if (col[i]^col[i-1]) ans++;
if (!head[col[i]]) st[col[i]]=i;
cnt[col[i]]++; nxt[i]=head[col[i]]; head[col[i]]=i;
}
rep(i, 1, m)
{
int opt=read();
if (opt==2) printf("%d
", ans);
else
{
int x=read(), y=read();
if (x==y) continue;
if (cnt[now[x]]>cnt[now[y]]) swap(now[x], now[y]);
x=now[x]; y=now[y];
if (cnt[x]) merge(x, y);
}
}
return 0;
}
例题2
思路
考虑一条链,显然你是把两个链分别的最大值放在一起,次大值放在一起,等等
那么如果有多个链呢?你就把第一个链和第二个链按上面的操作,得到的新的结果再和第三个链合并...
代码
#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=2e5+5;
int a[maxn],id[maxn],tot,tp[maxn];
priority_queue<int> q[maxn];
inline int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){if(ch==‘-‘) f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-‘0‘;ch=getchar();}
return x*f;
}
struct Edge{
int to,next;
}e[maxn<<1];
int head[maxn],cnt;
void add(int u,int v){
e[++cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt;
}
inline void dfs(int now,int fa){
id[now]=++tot;
for(int i=head[now];i;i=e[i].next)
{
int v=e[i].to;
if(v==fa) continue;
dfs(v,now);
if(q[id[now]].size()<q[id[v]].size()) swap(id[now],id[v]);
int size=q[id[v]].size();
for(int j=1;j<=size;j++)
{
tp[j]=max(q[id[now]].top(),q[id[v]].top());
q[id[now]].pop();
q[id[v]].pop();
}
for(int j=1;j<=size;j++) q[id[now]].push(tp[j]);
}
q[id[now]].push(a[now]);
}
int main(){
int n;
n=read();
for(int i=1;i<=n;i++)
a[i]=read();
for(int i=2;i<=n;i++)
{
int f;
f=read();
add(i,f);
add(f,i);
}
dfs(1,0);
ll ans=0;
while(q[id[1]].size()) ans+=q[id[1]].top(),q[id[1]].pop();
printf("%lld",ans);
return 0;
}
以上是关于启发式合并的主要内容,如果未能解决你的问题,请参考以下文章
bzoj2733: [HNOI2012]永无乡(splay+启发式合并/线段树合并)
#线段树合并树上启发式合并#CF600E Lomsat gelral