[bzoj1455]羅馬遊戲_左偏樹_並查集
阿新 • • 發佈:2018-04-16
gen ++ round span 大根堆 -s || () CP
羅馬遊戲 bzoj-1455
題目大意:給你n個人,2種操作,m次操作:1.將i號士兵所在的集合的最小值刪除 2.合並i和j兩個士兵所在的團體
註釋:$1\le n\le 10^6$,$1\le m \le 10^5$。
想法:又是GXZlegend講課,可並堆中的左偏樹。了解一下:
一個具有堆性質的二叉樹滿足任意一個節點x中,dis[lson[x]]>=dis[rson[x]],其中,dis表示當前節點一直走右兒子的最長步數。合並是遞歸合並,我們通過遞歸處理一兩個節點為根節點的左偏樹的合並,顯然左偏樹的子樹仍是左偏樹。我們直接將一顆子樹往另一顆子樹的有兒子上掛,這兩顆子樹根節點大的(默認大根堆)當做合並後的根節點即可。
附上合並代碼... ...
void merge(int x,int y) { if(!x) return y; if(!y) return x; if(val[x]<val[y]) swap(x,y); rson[x]=merge(rson[x],y); if(dis[rson[x]]>dis[lson[x]]) swap(rson[x],lson[x]); dis[x]=dis[rson[x]]+1; return x; }
至於這道題,我們用並查集維護每個人在哪個隊伍裏即可
最後,附上醜陋的代碼... ...
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define N 1000010 using namespace std; int fa[N],rson[N],lson[N],dis[N],w[N]; bool k[N]; int find(int x) { return fa[x]==x?x:(fa[x]=find(fa[x])); } int merge(int x,int y) { if(!x) return y; if(!y) return x; if(w[x]>w[y]) swap(x,y); rson[x]=merge(rson[x],y); if(dis[rson[x]]>dis[lson[x]]) { swap(lson[x],rson[x]); } dis[x]=dis[rson[x]]+1; return x; } inline int Kill(int x) { if(k[x]) return 0; x=find(x); int t=merge(lson[x],rson[x]); fa[x]=t; fa[t]=t; k[x]=true; return w[x]; } int main() { int n,m; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&w[i]); } for(int i=1;i<=n;i++) { fa[i]=i; } char s[3]; scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%s",s+1); if(s[1]==‘M‘) { int x,y; scanf("%d%d",&x,&y); if(k[x]||k[y]) continue; x=find(x),y=find(y); if(x!=y) fa[x]=fa[y]=merge(x,y); // fa[y]=x; } else { int x; scanf("%d",&x); printf("%d\n",Kill(x)); } } return 0; } /* 2 1 2 3 M 1 2 K 1 K 2 */
小結:錯誤都比較奇葩,在Kill的時候fa更新錯了,merge函數在寫的時候註意退出條件,不是任何時候都輸出x的。
[bzoj1455]羅馬遊戲_左偏樹_並查集