HDU 5997 rausen loves cakes

线段树合并区间+启发式合并。

针对合并操作,如果暴力合并,复杂度显然是O(n^2)O(n2)的,尝试用启发式合并优化它。 用数组维护每种颜色的位置个数,合并时,将个数少的颜色全部修改成个数多的颜色。 由于具体实现仍与暴力合并类似,因此可以轻易地维护前面提到的树状数组。 需要注意的是,由于合并时可能交换颜色,因此还需要维护每个数代表的真实颜色。 由于采用了启发式合并,因此时间复杂度为O(nlogn+Qlog^2n)O(nlogn+Qlog2n)O(nlogn+Qlogn)O(nlogn+Qlogn)。 本题也可以用线段树或其他很多数据结构解决。


代码:

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 1e5+5, MAXCOLOR = 1e6+5;
int qx, qy, color[MAXCOLOR];
struct node {
    int l, r, val;
}a[5*MAXN];
vector<int> record[MAXCOLOR];

void ini() {
    for(int i = 0; i < MAXCOLOR; i++) record[i].clear(), color[i] = i;
}

void mantain(int o, int l, int r) {
    int lson = o<<1, rson = o<<1|1;
    a[o].l = a[lson].l;
    a[o].r = a[rson].r;
    a[o].val = a[lson].val+a[rson].val;
    if(a[lson].r == a[rson].l) a[o].val--;
}

void build(int o, int l, int r) {
    if(l == r) {
        scanf("%d", &a[o].l);
        a[o].r = a[o].l;
        a[o].val = 1;
        record[a[o].l].push_back(l);
        return;
    }
    int m = (l+r)>>1;
    build(o<<1, l, m);
    build(o<<1|1, m+1, r);
    mantain(o, l, r);
}

int query(int o, int l, int r) {
    int lson = o<<1, rson = o<<1|1, ql = qx, qr = qy;
    if(l>=ql && r<=qr) return a[o].val;
    int m = (l+r)>>1, sum = 0, cnt = 0;
    if(m < qr) sum += query(rson, m+1, r), cnt++;
    if(m >= ql) sum += query(lson, l, m), cnt++;
    if(cnt==2 && a[lson].r==a[rson].l) sum--;
    return sum;
}

void update(int o, int l, int r, int p, int y) {
    int lson = o<<1, rson = o<<1|1, m = (l+r)>>1;
    if(l == r) {
        a[o].l = a[o].r = y;
        return;
    }
    if(m < p) update(rson, m+1, r, p, y);
    else update(lson, l, m, p, y);
    mantain(o, l, r);
}

int main() {
    int T;
    cin >> T;
    while(T--) {
        ini();
        int n, q;
        scanf("%d%d", &n, &q);
        build(1, 1, n);
        while(q--) {
            int op;
            scanf("%d%d%d", &op, &qx, &qy);
            if(op == 1) {
                int x = color[qx], y = color[qy];
                if(x == y) continue;
                if(record[x].size() < record[y].size()) {
                    for(int i = 0; i < record[x].size(); i++) {
                        int pos = record[x][i];
                        update(1, 1, n, pos, y);
                        record[y].push_back(pos);
                    }
                    record[x].clear();
                }
                else {
                    color[qy] = x, color[qx] = y;
                    for(int i = 0; i < record[y].size(); i++) {
                        int pos = record[y][i];
                        update(1, 1, n, pos, x);
                        record[x].push_back(pos);
                    }
                    record[y].clear();
                }
            }
            else printf("%d\n", query(1, 1, n));
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值