线段树区间排序问题两道经典问题 UESTC 1919 和 CF 558E

CF - 558E
题意: 给定一个字符串, q次操作, 每次操作一个区间, 0表示让这个区间降序, 1 表示升序, 问最后字符串的样子.

思路: 区间排序问题, 凡是区间排序问题, 并且里面涉及到的种类不多大概60左右, 都是可以用线段树维护的, 并且种类越少越好排, 所以我们直接用线段树维护当前这个区间每个种类的数量, 这里只有26种, 然后对于每次排序, 我们首先计算出这个区间的每一种种类数量, 然后再一一区间赋值, 赋值回去就很好的维护好了.

AC Code

const int maxn = 1e5+5;
char a[maxn];
struct node{
    int tl, tr; ll v[26], lazy;
    void fun(ll tmp) {
        lazy = tmp;
        for (int i = 0 ; i < 26 ; i ++) v[i] = 0;
        v[tmp] = tr - tl + 1;
    }
} tre[maxn<<2];
void pushup(int id) {
    for (int i = 0 ; i < 26 ; i ++)
        tre[id].v[i] = tre[id<<1].v[i] + tre[id<<1|1].v[i];
}
void pushdown(int id) {
    if(tre[id].lazy != -1) {
        tre[id<<1].fun(tre[id].lazy);
        tre[id<<1|1].fun(tre[id].lazy);
        tre[id].lazy = -1;
    }
}
void build(int id,int l,int r) {
    tre[id].tl = l; tre[id].tr = r; tre[id].lazy = -1;
    for (int i = 0 ; i < 26 ; i ++) tre[id].v[i] = 0;
    if(l == r) {
        tre[id].v[a[l]-'a'] = 1;
        return ;
    }
    int mid = (l+r) >> 1;
    build(id<<1, l, mid);
    build(id<<1|1, mid+1, r);
    pushup(id);
}
void update(int id, int ul, int ur, int val) {
    int l = tre[id].tl, r = tre[id].tr;
    if(ul <= l && r <= ur) {
        tre[id].fun(val);
        return ;
    }
    pushdown(id);
    int mid = (l+r) >> 1;
    if(ul <= mid) update(id<<1, ul, ur, val);
    if(ur > mid) update(id<<1|1, ul, ur, val);
    pushup(id);
}
int query(int id, int ql, int qr, int f, int ans) {
    int l = tre[id].tl , r = tre[id].tr;
    if(ql <= l && r <= qr) {
        if (ans) {
            for (int i = 0 ; i < 26 ; i ++) {
                if (tre[id].v[i]) return i;
            }
        }
        return tre[id].v[f];
    }
    pushdown(id);
    int mid = (l+r) >> 1 ;
    if(qr <= mid) return query(id<<1, ql, qr, f, ans);
    else if(ql > mid) return query(id<<1|1, ql, qr, f, ans);
    else return query(id<<1, ql, mid, f, ans) + query(id<<1|1, mid+1, qr, f, ans);
}
int cnt[26];
void solve() {
    int n, q;
    while(~scanf("%d%d", &n, &q)) {
        scanf("%s", a+1); build(1, 1, n);
        while(q--) {
            int l, r, op;
            scanf("%d%d%d", &l, &r, &op);
            for (int i = 0 ; i < 26 ; i ++) {
                cnt[i] = query(1, l, r, i, 0);
            }
            if (op) {
                for (int i = 0 ; i < 26 ; i ++) {
                    if (!cnt[i]) continue;
                    update(1, l, l+cnt[i]-1, i);
                    l += cnt[i];
                }
            }
            else {
                for (int i = 25 ; i >= 0 ; i --) {
                    if (!cnt[i]) continue;
                    update(1, l, l+cnt[i]-1, i);
                    l += cnt[i];
                }
            }
        }
        for (int i = 1 ; i <= n ; i ++) {
            printf("%c", query(1, i, i, 0, 1)+'a');
        }
        puts("");
    }
}

UESTC - 1919
题意: 每次给出n个数字(是n的一个排列), 和一个k, 然后进行q次操作,每次操作排序一个区间, 问最后的数组的第k位上面的数字是多少.

思路:因为前面已经说过了, 种类越少越好排, 怎么压缩种类了?我们再看一下是否具有二分性质?假设我们二分了一个答案, 我们将这个数组中>=ans的赋值为1, 否则赋值为0, 然后这只有两种种类了, 进行q次排序肯定很好维护, 那么怎么check? 如果最后第k位上是1, 那么当前答案肯定是满足的, 因为>=都是1, 那么比ans小的数满足嘛?肯定也是满足的, 因为假设这个就是答案, 那么更小的数也会使这个答案在第k位时依旧时1, 所以满足二分性质, 然后二分套线段树维护即可.

AC Code

const int maxn = 1e5+5;
int a[maxn];
struct node{
    int tl, tr; ll v[2], lazy;
    void fun(ll tmp) {
        lazy = tmp;
        v[tmp] = tr - tl + 1;
        v[tmp^1] = 0;
    }
} tre[maxn<<2];
void pushup(int id) {
    for (int i = 0 ; i < 2 ; i ++)
        tre[id].v[i] = tre[id<<1].v[i] + tre[id<<1|1].v[i];
}
void pushdown(int id) {
    if(tre[id].lazy != -1) {
        tre[id<<1].fun(tre[id].lazy);
        tre[id<<1|1].fun(tre[id].lazy);
        tre[id].lazy = -1;
    }
}
void build(int id,int l,int r, int x) {
    tre[id].tl = l; tre[id].tr = r; tre[id].lazy = -1;
    tre[id].v[0] = tre[id].v[1] = 0;
    if(l == r) {
        tre[id].v[a[l]>=x] = 1;
        return ;
    }
    int mid = (l+r) >> 1;
    build(id<<1, l, mid, x);
    build(id<<1|1, mid+1, r, x);
    pushup(id);
}
void update(int id, int ul, int ur, int val) {
    int l = tre[id].tl, r = tre[id].tr;
    if(ul <= l && r <= ur) {
        tre[id].fun(val);
        return ;
    }
    pushdown(id);
    int mid = (l+r) >> 1;
    if(ul <= mid) update(id<<1, ul, ur, val);
    if(ur > mid) update(id<<1|1, ul, ur, val);
    pushup(id);
}
int query(int id, int ql, int qr, int f) {
    int l = tre[id].tl , r = tre[id].tr;
    if(ql <= l && r <= qr) {
        return tre[id].v[f];
    }
    pushdown(id);
    int mid = (l+r) >> 1 ;
    if(qr <= mid) return query(id<<1, ql, qr, f);
    else if(ql > mid) return query(id<<1|1, ql, qr, f);
    else return query(id<<1, ql, mid, f) + query(id<<1|1, mid+1, qr, f);
}
struct QQ {
    int o, l, r;
}op[maxn];
void solve() {
    int n, k;
    while(~scanf("%d%d", &n, &k)) {
    for(int i = 1 ; i <= n ; i ++) {
        scanf("%d", &a[i]);
    }
    int q; scanf("%d", &q);
    for (int i = 1 ; i <= q ; i ++) {
        scanf("%d%d%d", &op[i].o, &op[i].l, &op[i].r);
    }
    int l = 1, r = n, ans = -1;
    while(r >= l) {
        int mid = (l + r) >> 1;
        build(1, 1, n, mid);
        for (int i = 1 ; i <= q ; i ++) {
            if (!op[i].o) {
                int num = query(1, op[i].l, op[i].r, 0);
                if (!num || num == op[i].r - op[i].l + 1) continue;
                update(1, op[i].l, op[i].l+num-1, 0);
                update(1, op[i].l+num, op[i].r, 1);
            }
            else {
                int num = query(1, op[i].l, op[i].r, 1);
                if (!num || num == op[i].r - op[i].l + 1) continue;
                update(1, op[i].l, op[i].l+num-1, 1);
                update(1, op[i].l+num, op[i].r, 0);
            }
        }
        if (query(1, k, k, 1)) {
            ans = mid;
            l = mid+1;
        }
        else r = mid - 1;
    }
    printf("%d\n", ans);
    }
}
互联网络程序设计是指在互联网上进行程序开发和设计的过程。UESTC则是我国的一所著名高校——电子科技大学。 互联网络程序设计 uestc包含了两个主要的方面:互联网络和程序设计。互联网络是指将多个计算机网络通过通信链路互相连接起来,实现信息共享和资源共享的网络系统。程序设计是指根据需求和目标,通过编写代码和设计算法,实现计算机程序的过程。 互联网络程序设计 uestc的学习内容主要包括以下几个方面: 1. 网络知识:学习互联网络的基本概念、原理和协议,如TCP/IP协议、HTTP协议等。掌握网络编程的基本技术,能够编写网络应用程序。 2. 数据通信:学习数据通信的基本原理和技术,包括数据传输的方式、数据压缩和加密等。了解网络安全和数据保护的基本知识。 3. 程序设计:学习编程语言和开发工具,如Java、C++和Python等。掌握常用的编程技巧和方法,能够设计和实现复杂的网络应用程序。 4. Web开发:学习Web开发的基本知识和技术,包括HTML、CSS、JavaScript等。能够设计和实现交互式的Web应用程序。 5. 数据库技术:学习数据库的基本原理和技术,如SQL语言和数据库管理系统。能够设计和管理数据库,实现数据的存储和检索。 通过学习互联网络程序设计 uestc,可以掌握互联网应用开发的基本技能,具备设计和实现网络应用程序的能力。这对于目前互联网行业的人才需求来说是非常重要的,也为学生提供了广阔的就业和创业机会。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值