【BZOJ1483】[HNOI2009]夢幻布丁(平衡樹啟發式合併+並查集)
阿新 • • 發佈:2018-12-03
題目:
分析:
(這題碼了一下午,碼了近250行,但是意外跑的比本校各位神仙稍快,特寫部落格紀念)
首先能看出一個顯然的結論:顏色段數只會變少不會變多。
我們考慮用並查集維護區間,對於每個區間維護它的起點和終點。建\(n\)棵平衡樹,第\(i\)棵存顏色為\(i\)的區間。把\(x\)變成\(y\)時進行啟發式合併,同時對於\(x\)上的每個結點\([a,b]\),在\(y\)中找\(a-1\)和\(b+1\)所在區間。如果存在則合併,並答案減\(1\);若不存在則向\(y\)中插入新結點。時間複雜度\(O(m\log ^2n)\)
程式碼:
程式碼不難寫,就是長……
#include <cstdio> #include <cstring> #include <cctype> #include <algorithm> using namespace std; namespace zyt { template<typename T> inline void read(T &x) { char c; bool f = false; x = 0; do c = getchar(); while (c != '-' && !isdigit(c)); if (c == '-') f = true, c = getchar(); do x = x * 10 + c - '0', c = getchar(); while (isdigit(c)); if (f) x = -x; } template<typename T> inline void write(T x) { static char buf[20]; char *pos = buf; if (x < 0) putchar('-'), x = -x; do *pos++ = x % 10 + '0'; while (x /= 10); while (pos > buf) putchar(*--pos); } const int N = 1e6 + 10; int n, m, p[N], st[N], ed[N]; int f(const int x) { return x == p[x] ? x : p[x] = f(p[x]); } int ans = 0; class Splay { private: struct node { int val, size; node *fa, *s[2]; node(const int _val, node *_fa) : val(_val), fa(_fa) { size = 1; s[0] = s[1] = NULL; } }*head; bool dir(const node *rot) { return rot == rot->fa->s[1]; } void update(node *rot) { rot->size = 1; if (rot->s[0]) rot->size += rot->s[0]->size; if (rot->s[1]) rot->size += rot->s[1]->size; } void rotate(node *rot) { node *f = rot->fa, *ff = f->fa; bool d = dir(rot); rot->fa = ff; if (ff) ff->s[dir(f)] = rot; else head = rot; f->s[d] = rot->s[!d]; if (rot->s[!d]) rot->s[!d]->fa = f; rot->s[!d] = f; f->fa = rot; update(f); } void splay(node *rot, const node *goal = NULL) { while (rot && rot->fa && rot->fa != goal) { node *f = rot->fa, *ff = f->fa; if (ff == goal) rotate(rot); else if (dir(rot) ^ dir(f)) rotate(rot), rotate(rot); else rotate(f), rotate(rot); } update(rot); } void del(node *rot, Splay &s) { if (!rot) return; int x = f(rot->val); bool flag = false; if (st[x] > 1 && s.find(f(st[x] - 1))) { p[x] = f(st[x] - 1); ed[f(st[x] - 1)] = ed[x]; --ans, flag = true; } x = f(x); if (ed[x] < n && s.find(f(ed[x] + 1))) { p[x] = f(ed[x] + 1); st[f(ed[x] + 1)] = st[x]; --ans, flag = true; } if (!flag) s.insert(rot->val); if (rot->s[0]) del(rot->s[0], s); if (rot->s[1]) del(rot->s[1], s); delete rot; } node *find(const int val) { node *rot = head; while (1) { if (!rot || rot->val == val) return rot; if (val < rot->val) rot = rot->s[0]; else rot = rot->s[1]; } } public: void insert(const int val) { if (!head) { head = new node(val, NULL); return; } node *rot = head; while (1) { if (val < rot->val) { if (rot->s[0]) rot = rot->s[0]; else { rot->s[0] = new node(val, rot); splay(rot->s[0]); break; } } else { if (rot->s[1]) rot = rot->s[1]; else { rot->s[1] = new node(val, rot); splay(rot->s[1]); break; } } } } size_t size() { if (head) return head->size; else return 0; } friend void merge(Splay &a, Splay &b); }tree[N]; inline void merge(Splay &a, Splay &b) { if (a.size() < b.size()) swap(a, b); b.del(b.head, a); b.head = NULL; } int arr[N], tmp[N]; int work() { read(n), read(m); int last = 1; for (int i = 1; i <= n; i++) read(arr[i]); p[1] = st[1] = ed[1] = 1; for (int i = 2; i <= n; i++) { if (arr[i] != arr[i - 1]) { tree[arr[i - 1]].insert(last); st[i] = last = i, ++ans; } p[i] = last; ed[last] = i; } tree[arr[n]].insert(last), ++ans; while (m--) { int opt; read(opt); if (opt == 1) { int x, y; read(x), read(y); if (x != y) merge(tree[y], tree[x]); } else write(ans), putchar('\n'); } return 0; } } int main() { return zyt::work(); }