BZOJ1483——[HNOI2009]梦幻布丁

1、题目大意:这题就是给你一个序列,有两个操作,一个是询问序列中的连续段数,比如序列 1 2 2 1就是三段。。
1是一段,2 2 又是一段,1又是一段,就是相同的在一起,第二个操作就是将其中的一种数全都改成另一种数
2、分析:这道题看起来做需要o(n^2),这是过不了的,我们需要nlogn的算法,怎么实现修改是均摊logn的呢。。
我们把两个链表合并是O(1)这个是一定的,但是修改答案是o(n)的对吧,那怎么办呢,
我们算修改答案把len小的合并到大的,仔细算算,其实算不了几次,这样就o(n),
我们还要存一个编号,就是一个数在链表里的编号

这样,我们就可以ac了

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
int h[2000000];
int num[2000000];
int head[2000000], ne[2000000];
int len[2000000];
int main(){
    memset(head, -1, sizeof(head));
    int n, m;
    scanf("%d%d", &n, &m);
    int ans = 0;
    for(int i = 1; i <= n; i ++){
        scanf("%d", &h[i]);
        ne[i] = head[h[i]];
        head[h[i]] = i;
        num[h[i]] = h[i];
        if(h[i] != h[i - 1]) ans ++;
        len[h[i]] ++;
    }
    for(int i = 1; i <= m; i ++){
        int op;
        scanf("%d", &op);
        if(op == 2) printf("%d\n", ans);
        else {
            int a, b;
            scanf("%d%d", &a, &b);
            if(a == b) continue;
            if(len[num[a]] > len[num[b]]) swap(num[a], num[b]);
            a = num[a];
            b = num[b];
            if(len[a] == 0) continue;
            for(int j = head[a]; j != -1; j = ne[j]){
                if(h[j - 1] == b) ans --;
                if(h[j + 1] == b) ans --;
            }
            int p;
            for(int j = head[a]; j != -1; j = ne[j]){
                h[j] = b;
                p = j;
            }
            ne[p] = head[b];
            head[b] = head[a]; 
            len[b] += len[a];
            len[a] = 0; 
            head[a] = -1;
        }
    }
    return 0;
} 


你可能感兴趣的:(乱搞,bzoj,HNOI2009,1483,梦幻布丁)