【启发式合并】梦幻布丁(金牌导航 启发式合并-1/luogu 3201)
夢幻布丁
金牌導航 啟發(fā)式合并-1
luogu 3201
題目大意
有若干個布丁,給出它們的顏色,每次將一個顏色的所有布丁變成另一種顏色,然后詢問有多少段連續(xù)的數(shù)
輸入樣例
4 3 1 2 2 1 2 1 2 1 2輸出樣例
3 1樣例解釋
初始時布丁顏色依次為 1,2,2,1,三段顏色分別為 [1,1], [2,3], [4,4]
一次操作后,布丁的顏色變?yōu)?,1,1,1,只有[1,4]一段顏色
數(shù)據(jù)范圍
1?n,m?105,1?ai,x,y?1061 \leqslant n, m \leqslant 10^5,1 \leqslant a_i ,x, y \leqslant 10^61?n,m?105,1?ai?,x,y?106
提示
請注意,不保證顏色的編號不大于 n,也不保證 x≠yx \neq yx?=y,m 不是顏色的編號上限
解題思路
如果每次暴力查詢時間O(nm)O(nm)O(nm),會TLETLETLE
對于每次合并,考慮把小的合并到大的中,這樣可以有效降低時間
對于集合AAA,如果和BBB合并(∣A∣?∣B∣)(|A|\leqslant|B|)(∣A∣?∣B∣),那么AAA所在集合大小變?yōu)?span id="ze8trgl8bvbq" class="katex--inline">∣A∣+∣B∣|A|+|B|∣A∣+∣B∣,變大了一倍,所以集合∣A∣|A|∣A∣最多合并lognlognlogn次,nnn個集合時間為O(nlogn)O(nlogn)O(nlogn)
對于更改顏色,可以用一個b數(shù)組存下第i種顏色存在第幾個顏色中,這樣可以O(shè)(1)實現(xiàn)交換兩個子集,就可以使小的合并到大的中
代碼
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll long long #define N 100010 using namespace std; int n, m, x, y, ans, s[N*10], b[N*10], st[N*10], nx[N], head[N*10], color[N]; void add(int x, int y)//加入點 {b[x] = x;//初始存在自己if (!head[x]) st[x] = y;//第一條邊nx[y] = head[x];head[x] = y;s[x]++; } void solve(int x, int y) {for (int i = head[x]; i; i = nx[i]){if (y == color[i - 1]) ans--;//相同就合并了,數(shù)量-1if (y == color[i + 1]) ans--;}for (int i = head[x]; i; i = nx[i])color[i] = y;//、、變色s[y] += s[x];//累加nx[st[y]] = head[x];//連在該子集后面st[y] = st[x];head[x] = s[x] = st[x] = 0; } int main() {scanf("%d%d", &n, &m);for (int i = 1; i <= n; ++i){scanf("%d", &color[i]);if (color[i] != color[i - 1]) ans++;add(color[i], i);}while(m--){scanf("%d", &x);if (x == 2) printf("%d\n", ans);else{scanf("%d%d", &x, &y);if (x == y) continue;if (s[b[x]] > s[b[y]]) swap(b[x], b[y]);//小的合并到大的if (!s[b[x]]) continue;solve(b[x], b[y]);}}return 0; }總結(jié)
以上是生活随笔為你收集整理的【启发式合并】梦幻布丁(金牌导航 启发式合并-1/luogu 3201)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 任天堂宣布为 3DS / Wii U 主
- 下一篇: 【线性基】彩灯(luogu 3857/金