题目:排序(二分&线段树)

Posted loney-s

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题目:排序(二分&线段树)相关的知识,希望对你有一定的参考价值。

题目

传送门

思路

此题两个思路

卡着过

表面上看这是一道紫题,但是数据真的水
用心造题,用脚造数据
实际上桶排就能过
但是需要加一个小优化
对于每次排序的区间
只需要从区间的最小值枚举到最大值就行了

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<algorithm>
using namespace std;
void read(int &x)
{
    x=0;
    int f=1;
    char c=getchar();
    while('0'>c||c>'9')
    {
        if(c=='-')
            f=-1;
        c=getchar();
    }
    while('0'<=c&&c<='9')
    {
        x=(x<<3)+(x<<1)+c-'0';
        c=getchar();
    }
    x*=f;
}
void write(int x)
{
    if(x<10)
        putchar(x+'0');
    else
    {
        write(x/10);
        putchar(x%10+'0');
    }
}
int n,m;
int minn;
int maxx;
int a[100005];
int cm;
int be;
int ne;
int q;
bool t[100005];
inline void sortt(int l,int r,int f)
{
    minn=INT_MAX;
    maxx=INT_MIN;
    for(int i=l;i<=r;i++)
    {
        t[a[i]]=1;
        minn=min(minn,a[i]);
        maxx=max(maxx,a[i]);
    }
    if(f==0)
    {
        for(int i=minn;i<=maxx;i++)
        {
            if(t[i])
            {
                a[l++]=i;
                t[i]=0;
            }
        }
    }
    else
    {
        for(int i=maxx;i>=minn;i--)
            if(t[i])
            {
                a[l++]=i;
                t[i]=0;
            }
    }
}
int main()
{
    read(n);
    read(m);
    for(int i=1;i<=n;i++)
        read(a[i]);
    for(int i=1;i<=m;i++)
    {
        read(cm);
        read(be);
        read(ne);
        sortt(be,ne,cm);
        /*for(int j=1;j<=n;j++)
        {
            cout<<a[j]<<' ';
        }
        cout<<'
';*/
    }
    read(q);
    write(a[q]);
    return 0;
}   

正解

题目上有一个很有意思的点,也是本题的突破口
我们只需要输出(a_q)就行了
将序列重新构造成为一个01序列
如果(a_i<x)
(b_i==0)
否则(b_i==1)
这样子序列的排列就很简单了
如果是从小到大,就直接000011111
或者就11100000
之后我们查(a_q)的值,如果(a_q<x)(ans<x)
反之则(ans>x)
也就是我们可以将最后的答案通过二分确定在一个范围之内

代码(直接贴trymyedge发的STD)

#include <cstdio>
#include <cstring>
#include <cctype>
#define lc o << 1
#define rc o << 1 | 1
#define mid (l + r) / 2
using namespace std;

const int N = 100010;
int n, m, p;
int T[4 * N], lazy[4 * N];    // segment tree
int a[N], ch[N], L[N], R[N];  // the information by reading

inline int read() {
    char ch = getchar();
    int x = 0;
    while (!isdigit(ch)) ch = getchar();
    while (isdigit(ch)) {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x;
}

inline void build(int o, int l, int r, int x) {
    if (l == r) {
        T[o] = a[l] >= x;
        lazy[o] = 0;
        return;
    }
    build(lc, l, mid, x);
    build(rc, mid + 1, r, x);
    T[o] = T[lc] + T[rc];
    lazy[o] = 0;
}

inline void pushdown(int o, int l, int r) {
    if (!lazy[o])
        return;
    lazy[lc] = lazy[rc] = lazy[o];
    if (lazy[o] == 1) {
        T[lc] = mid - l + 1;
        T[rc] = r - mid;
    } else
        T[lc] = T[rc] = 0;
    lazy[o] = 0;
}

inline int query(int o, int l, int r, int x, int y) {
    if (x <= l && y >= r)
        return T[o];
    if (x > r || y < l)
        return 0;
    pushdown(o, l, r);
    return query(lc, l, mid, x, y) + query(rc, mid + 1, r, x, y);
}

inline int queryPoint(int o, int l, int r, int x) {
    if (l == x && r == x)
        return T[o];
    pushdown(o, l, r);
    if (x <= mid)
        return queryPoint(lc, l, mid, x);
    else
        return queryPoint(rc, mid + 1, r, x);
}

inline void update(int o, int l, int r, int x, int y, int val) {
    if (x <= l && y >= r) {
        T[o] = val * (r - l + 1);
        lazy[o] = val ? 1 : -1;
        return;
    }
    if (x > r || y < l)
        return;
    pushdown(o, l, r);
    update(lc, l, mid, x, y, val);
    update(rc, mid + 1, r, x, y, val);
    T[o] = T[lc] + T[rc];
}

inline bool check(int x) {
    build(1, 1, n, x);
    for (int i = 1; i <= m; i++) {
        int cnt1 = query(1, 1, n, L[i], R[i]);
        if (ch[i] == 0) {
            update(1, 1, n, R[i] - cnt1 + 1, R[i], 1);
            update(1, 1, n, L[i], R[i] - cnt1, 0);
        } else {
            update(1, 1, n, L[i], L[i] + cnt1 - 1, 1);
            update(1, 1, n, L[i] + cnt1, R[i], 0);
        }
    }
    return queryPoint(1, 1, n, p);
}

int main() {

    n = read();
    m = read();
    for (int i = 1; i <= n; i++) a[i] = read();
    for (int i = 1; i <= m; i++) {
        ch[i] = read();
        L[i] = read();
        R[i] = read();
    }
    p = read();
    int ll = 1, rr = n, midd, ans;
    while (ll <= rr) {
        midd = (ll + rr) >> 1;
        if (check(midd))
            ans = midd, ll = midd + 1;
        else
            rr = midd - 1;
    }
    printf("%d
", rr);
    return 0;
}

以上是关于题目:排序(二分&线段树)的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 4552 [Tjoi2016&Heoi2016]排序 (二分答案 线段树)

bzoj4552[Tjoi2016&Heoi2016]排序 二分+线段树

bzoj4552/Tjoi2016&Heoi2016排序——二分+线段树/平衡树+线段树分裂与合并

bzoj4552: [Tjoi2016&Heoi2016]排序(二分+线段树)

[BZOJ] 4552: [Tjoi2016&Heoi2016]排序 #二分+线段树+算法设计策略

bzoj 4552: [Tjoi2016&Heoi2016]排序——二分+线段树