LG2617

除了树套树做法外,整体二分也是可以的。

整体二分的主要思想是:通过对值域进行一次二分,来求多个区间排名,从而替代了树套树中的权值线段树。具体地,我们把所有操作按时间先后顺序确定后,假设当前的值域中点为 \(mid\),那么按原顺序进行所有修改值不超过 \(mid\) 的操作,用对应的下标更新树状数组。同时对于中间穿插的查询操作,利用树状数组求出 \([l,r]\) 内不超过 \(mid\) 的数的个数,再与 \(k\) 比较决定答案在 \(mid\) 的哪侧,递归下去即可。当值域缩小为一个数时,此时包含的查询的答案即为这个数。

一些注意点:

  • 修改操作要分解为删除和插入两步。

  • 初始输入的 \(a\) 应转为 \(n\) 个插入操作。

  • 将操作划分为左右两部分时要按原顺序存储,即时间顺序不能改变

离散化后,由于最多有 \(n+m\) 个不同的数,因此递归最多 \(\log_2(n+m)\) 层,总操作数 \(n+m\),每层执行所有操作时间复杂度 \(O((n+m) \log (n+m) )\),总时间复杂度 \(O((n+m)\log^2(n+m))\),实际最大点不到 300ms。(当然不离散化也可以)

#include <iostream>
#include <cstdio>
#include <algorithm>
#define N 200001

using namespace std;

int n,m,M,M2,a[N],ls[N * 2],siz,ans[N * 2],op2[N * 2];
int t[N * 2];

struct Q
{
    int op,l,r,k,x,y,id;
}q[N * 2],q1[N * 2],q2[N * 2];

int lowbit( int x )
{
    return x & ( -x );
}

void upd( int x , int y )
{
    for( int i = x ; i <= siz ; i += lowbit( i ) )
        t[i] += y;
    return;
}

int que( int x )
{
    int sum = 0;
    for( int i = x ; i >= 1 ; i-= lowbit( i ) )
        sum += t[i];
    return sum;
}

void solve( int l , int r , int ql , int qr )
{
    if( ql > qr ) return;
    if( l == r )
    {
        for( int i = ql ; i <= qr ; i ++ )
            if( q[i].op == 2 )
                ans[q[i].id] = l;
        return;        
    }
    int mid = ( l + r ) / 2,L = 0,R = 0;
    for( int i = ql ; i <= qr ; i ++ )
    {
        if( q[i].op == 0 )
        {
            if( q[i].y <= mid )
            {
                L ++;
                q1[L] = q[i];
                upd( q[i].x , 1 );
            }
            else
            {
                R ++;
                q2[R] = q[i];
            }
        }
        if( q[i].op == 1 )
        {
            if( q[i].y <= mid )
            {
                L ++;
                q1[L] = q[i];
                upd( q[i].x , -1 );
            }
            else
            {
                R ++;
                q2[R] = q[i];
            }
        }
        if( q[i].op == 2 )
        {
            int nw = que( q[i].r ) - que( q[i].l - 1 );
            if( nw >= q[i].k ) L ++,q1[L] = q[i];
            else R ++,q2[R] = q[i],q2[R].k -= nw;
        }
    }
    
    for( int i = ql ; i <= qr ; i ++ )
    {
        if( q[i].op == 0 && q[i].y <= mid ) upd( q[i].x , -1 );
        if( q[i].op == 1 && q[i].y <= mid ) upd( q[i].x , 1 );
    }
    int i = ql;
    for( int j = 1 ; j <= L ; i ++ , j ++ )
        q[i] = q1[j];
    for( int j = 1 ; j <= R ; i ++ , j ++ )
        q[i] = q2[j];
    solve( l , mid , ql , ql + L - 1 );
    solve( mid + 1 , r , ql + L , qr );
    return;
}

int main()
{
    ios::sync_with_stdio( false );
    cin.tie( 0 );
    cout.tie( 0 );
    cin >> n >> m;
    for( int i = 1 ; i <= n ; i ++ )
    {
        cin >> a[i];
        ls[++ siz] = a[i];
        M ++;
        q[M].id = M;
        q[M].op = 0;
        q[M].x = i;
        q[M].y = a[i];
    }
    int l,r,k,x,y;
    char op;
    for( int i = 1 ; i <= m ; i ++ )
    {
        cin >> op;
        if( op == 'C' )
        {
            cin >> x >> y;
            ls[++ siz] = y;
            M ++;
            q[M].op = 1;
            q[M].x = x;
            q[M].y = a[x];
            M ++;
            q[M].op = 0;
            q[M].x = x;
            q[M].y = y;
            a[x] = y;
        }
        else
        {
            M ++;
            M2 ++;
            cin >> l >> r >> k;
            q[M].op = 2;
            q[M].id = M2;
            q[M].l = l;
            q[M].r = r;
            q[M].k = k;
        }
    }
    sort( ls + 1 , ls + siz + 1 );
    siz = unique( ls + 1 , ls + siz + 1 ) - ls - 1;
    for( int i = 1 ; i <= M ; i ++ )
        if( q[i].op == 0 || q[i].op == 1 )
            q[i].y = lower_bound( ls + 1 , ls + siz + 1 , q[i].y ) - ls;
    solve( 1 , siz , 1 , M );
    for( int i = 1 ; i <= M2 ; i ++ )
        cout << ls[ans[i]] << '\n';
    return 0;
}
posted @ 2025-09-08 18:42  FormulaOne  阅读(8)  评论(0)    收藏  举报