啟發式合併
阿新 • • 發佈:2020-07-19
啟發式合併
概念
啟發式演算法是基於人類的經驗和直觀感覺,對一些演算法的優化。
作用
可以啟發式合併更加高階的資料結構,如 \(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\n", 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; }