HDU 5997 rausen loves cakes (线段树区间维护,启发式区间合并)

rausen loves cakes

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 83    Accepted Submission(s): 16


Problem Description
Rausen loves cakes. One day, he bought n cakes and the color of each cake is described as an integer in [1,1000000]. Rausen lines the cakes from left to right.

Before eating, rausen proceeds q operations on cakes.

At one time point, rausen would replace all cakes of x color with those of color y.

At another time point, rausen would calculate the number of segment colors in the interval [x,y]. A color segment is defined as an interval of one single color. For example,'1 4 4 1 1' involves 3 color segments.

Nevertheless, rausen finds that he cannot compile the statistics of color segments in the interval, which makes him weep like a helpless crybaby (bazinga). Please help rausen resolve the problem to placate him.
 

Input
There are multiple test cases. The first line of input contains an integer T indicating the number of test cases. For each test case:

The first line contains 2 integers n, q.

In the following q lines,each line contains 3 integers: op(1op2), x and y which describe one operation:

If op=1, then rausen is to proceed a substitution operation and this is when you replace cakes of color x with those of color y. x and y satisfy (1x,y1000000).

if op=2, then rausen is to proceed a counting operation and this is when you are required to input the color segments in the interval [x,y]. x and y satisfy (1xyn)

(1T5), (1n105), (1q105)
 

Output
For every counting operation of each case, a single line contains one number as the answer.
 

Sample Input
 
   
1 5 3 1 4 4 10 1 2 1 5 1 4 10 2 3 5
 

Sample Output
 
   
4 2
 

Source
BestCoder Round #90
 

Recommend
wange2014


这道题的大体题意就是给你N个数,对于这连续的N个数有两种操作,第一种操作是将某一种颜色改成另一种颜色,而第二种就是

询问从L到R的范围内颜色段(几个连续的且颜色都相同的点算作1个颜色段)的数目。

所以我们可以使用线段树加上启发式合并(对于要合并的两个区间,把小的区间合成到大的区间里,可以上网搜索一下),这样就

可以保证时间复杂度为 n*log(n)*log(n),这样的时间复杂度是可以接受的!具体看代码。


#include 
#include 
#include 
#include 
using namespace std;
const int maxn = 1e6 + 7;

struct node{ /// 线段树,分别需要维护的是这个区间的左右端点的位置,这段区间内的颜色段数与及左右端点的颜色
    int l, r, lc, rc, val;
} a[maxn<<2];
struct Item{ /// 链式前向星存点
    int data, next;
} keke[maxn];
int cnt, color[maxn], sum[maxn], record[maxn];

void init(){ /// 初始化
    memset(color, -1, sizeof( color));
    memset(sum, 0, sizeof( sum));
    for(int i = 1; i <= maxn-7; i ++) record[i] = i;
    cnt = 1;
}

void add(int w, int pos){ /// 前向星增加
    keke[cnt].data = pos;
    keke[cnt].next = color[w];
    color[w] = cnt ++;
    sum[w] ++;
}

void Buildtree(int key, int l, int r){ /// 建线段树
    a[key].l = l;
    a[key].r = r;
    if(l == r){
        int tt;
        scanf("%d", &tt);
        a[key].lc = a[key].rc = tt;
        a[key].val = 1;
        add(tt, l);
        return;
    }
    int mid = (l + r) >> 1;
    Buildtree(key<<1, l, mid);
    Buildtree(key<<1|1, mid + 1, r);
    a[key].val = a[key<<1].val + a[key<<1|1].val; /// 合并区间,维护区间内的颜色段数
    if(a[key<<1].rc == a[key<<1|1].lc) a[key].val --; /// 判断合并区间时左区间的右端点颜色和右区间左端点的颜色是否相同
    a[key].lc = a[key<<1].lc;
    a[key].rc = a[key<<1|1].rc;
}

node Query(int key, int l, int r){ /// 查询函数
    if(a[key].l == l && a[key].r == r) return a[key];
    int mid = (a[key].l + a[key].r) >> 1;
    if(r <= mid) return Query(key<<1, l, r);
    else if(l > mid) return Query(key<<1|1, l, r);
    else{  /// 和上面合并区间的方法雷同
        node t1 = Query(key<<1, l, mid), t2 = Query(key<<1|1, mid + 1, r);
        t1.val += t2.val;
        if(t1.rc == t2.lc) t1.val --; 
        t1.r = t2.r;
        t1.rc = t2.rc;
        return t1;
    }
}

void update(int key, int w, int pos){ /// 单点更新
    if(a[key].l == a[key].r && a[key].l == pos){    
        a[key].lc = a[key].rc = w;
        a[key].val = 1;
        return;
    }
    int mid = (a[key].l + a[key].r) >> 1;
    if(pos <= mid) update(key<<1, w, pos);
    else update(key<<1|1, w, pos);
    a[key].val = a[key<<1].val + a[key<<1|1].val; /// 单点更新后对于区间的维护
    if(a[key<<1].rc == a[key<<1|1].lc) a[key].val --;
    a[key].lc = a[key<<1].lc;
    a[key].rc = a[key<<1|1].rc;
}

void Merge(int p, int q){ /// 合并区间,这里有的是启发式合并
    int temp = color[p];
    for( ; keke[temp].next != -1; temp = keke[temp].next){
        update(1, record[q], keke[temp].data); /// 每次都暴力更新点
    }
    update(1, record[q], keke[temp].data);
    keke[temp].next = color[q];
    sum[p] += sum[q];
    color[q] = -1;
    sum[q] = 0;
    swap(record[p], record[q]);
}

//void Show(int key){
//    printf("%d %d %d %d %d %d\n", key, a[key].l, a[key].r, a[key].lc, a[key].rc, a[key].val);
//    if(a[key].l == a[key].r) return;
//    Show(key<<1);
//    Show(key<<1|1);
//}

int main(){
    int t, n, m, u, v, z;
    scanf("%d", &t);
    while(t --){
        scanf("%d %d", &n, &m);
        init();
        Buildtree(1, 1, n);
        while(m --){
            scanf("%d %d %d", &z, &u, &v);
            if(z == 1){
                if(u == v) continue;
                if(sum[u] && sum[v]){
                    if(sum[u] < sum[v]) Merge(u, v);
                    else Merge(v, u);
                }
                if(sum[u] > sum[v]){
                    swap(sum[u], sum[v]);
                    swap(color[u], color[v]);
                    swap(record[u], record[v]);
                }
            }
            else printf("%d\n", Query(1, u, v).val);
        }
    }
    return 0;
}

你可能感兴趣的:(线段树)