P2617 BIT + 主席树 / 整体二分

题意

传送门 P2617 Dynamic Rankings

题解
BIT + 主席树

求动态区间第 k k k 小,带修改的主席树裸题。考虑主席树修改某一个历史版本(即某一个前缀的权值线段树),那么也需要修改其后的历史版本,单次修改复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

B I T BIT BIT 单次修改前缀复杂度 O ( l o g n ) O(logn) O(logn),那么可以 B I T BIT BIT 套主席树。基本思想是 B I T BIT BIT 维护的区间为历史版本 [ 1 , n ] [1,n] [1,n],那么可以高效的进行单点修改; B I T BIT BIT 上每一个点对应一颗主席树,维护值域 [ 0 , u b ) [0,ub) [0,ub) 的信息,那么可以处理区间第 k k k 小的查询;此时主席树维护的不再是 a i a_i ai 的前缀信息,而是对应的 B I T BIT BIT 点的索引值二进制所代表区间的信息。单点修改,即 B I T BIT BIT 处理前缀同时更新主席树,复杂度 O ( l o g 2 n ) O(log^2n) O(log2n)

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
#define maxn 100005
#define tree_size 32400005
struct operation
{
    bool q;
    int l, r, k, x, y;
} op[maxn];
int N, M, K, A[maxn], S[maxn << 1];
int cnt, rt[maxn], cl[tree_size], cr[tree_size], sum[tree_size];
vector<int> tu, tv;

void update(int &cur, int x, int v, int l, int r)
{
    if (!cur)
        cur = ++cnt;
    sum[cur] += v;
    if (r - l > 1)
    {
        int m = (l + r) >> 1;
        x < m ? update(cl[cur], x, v, l, m) : update(cr[cur], x, v, m, r);
    }
}

void bit_update(int i, int v)
{
    int x = lower_bound(S, S + K, A[i]) - S;
    for (int j = i; j <= N; j += j & -j)
        update(rt[j], x, v, 0, K);
}

int query(int k, int l, int r)
{
    if (r - l == 1)
        return l;
    int s = 0, m = (l + r) >> 1;
    for (int i = 0; i < (int)tu.size(); ++i)
        s -= sum[cl[tu[i]]];
    for (int i = 0; i < (int)tv.size(); ++i)
        s += sum[cl[tv[i]]];
    if (k <= s)
    {
        for (int i = 0; i < (int)tu.size(); ++i)
            tu[i] = cl[tu[i]];
        for (int i = 0; i < (int)tv.size(); ++i)
            tv[i] = cl[tv[i]];
        return query(k, l, m);
    }
    else
    {
        for (int i = 0; i < (int)tu.size(); ++i)
            tu[i] = cr[tu[i]];
        for (int i = 0; i < (int)tv.size(); ++i)
            tv[i] = cr[tv[i]];
        return query(k - s, m, r);
    }
}

int bit_query(int u, int v, int k)
{
    tu.clear();
    tv.clear();
    for (int j = u; j > 0; j -= j & -j)
        tu.push_back(rt[j]);
    for (int j = v; j > 0; j -= j & -j)
        tv.push_back(rt[j]);
    return query(k, 0, K);
}

int main()
{
    scanf("%d%d", &N, &M);
    for (int i = 1; i <= N; ++i)
    {
        scanf("%d", A + i);
        S[K++] = A[i];
    }
    for (int i = 0; i < M; ++i)
    {
        char c;
        scanf(" %c", &c);
        op[i].q = c == 'Q';
        if (op[i].q)
            scanf("%d%d%d", &op[i].l, &op[i].r, &op[i].k);
        else
        {
            scanf("%d%d", &op[i].x, &op[i].y);
            S[K++] = op[i].y;
        }
    }
    sort(S, S + K);
    K = unique(S, S + K) - S;
    for (int i = 1; i <= N; ++i)
        bit_update(i, 1);
    for (int i = 0; i < M; ++i)
    {
        if (op[i].q)
            printf("%d\n", S[bit_query(op[i].l - 1, op[i].r, op[i].k)]);
        else
        {
            int j = op[i].x;
            bit_update(j, -1);
            A[j] = op[i].y;
            bit_update(j, 1);
        }
    }
    return 0;
}
整体二分

整体二分的基本思路即,将全部的查询分治处理。考虑单次查询区间第 k k k 小,可以按值域进行分治。对于多次查询,可以根据区间中值划分查询至左右区间(统计 a i a_i ai 小于值域区间中值的数量, B I T BIT BIT 维护前缀和即可)。对于 a i a_i ai 的单点修改,则按照时间顺序存储,此时将原始 a i a_i ai 看做多次修改,且时间顺序最靠前;对 a i a_i ai 原值(若 a i < m i d a_i<mid ai<mid)对应地在 B I T BIT BIT 上减一,再将修改值(若 a i < m i d a_i<mid ai<mid)在 B I T BIT BIT 上加一。将 a i a_i ai 与修改值离线下来,离散化处理,总的时间复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
#define maxn 100005
struct operation
{
    bool q;
    int x, y, z, i; //q = 1 : l, r, k, i;  q = 0 : i, x, v
    operation(bool q, int x, int y, int z, int i) : q(q), x(x), y(y), z(z), i(i) {}
};
int N, Q, M, A[maxn], B[maxn << 1], bit[maxn], res[maxn];
vector<operation> op;

void add(int i, int x)
{
    while (i <= N)
    {
        bit[i] += x;
        i += i & -i;
    }
}

int sum(int i)
{
    int s = 0;
    while (i > 0)
    {
        s += bit[i];
        i -= i & -i;
    }
    return s;
}
void solve(vector<operation> &op, int l, int r)
{
    if (op.empty() || r - l == 1)
    {
        for (auto &q : op)
            if (q.q)
                res[q.i] = l;
        return;
    }
    vector<operation> left, right;
    int m = (l + r) >> 1;
    for (auto &q : op)
    {
        int m = (l + r) >> 1;
        if (q.q)
        {
            int s = sum(q.y) - sum(q.x - 1);
            if (q.z <= s)
                left.emplace_back(1, q.x, q.y, q.z, q.i);
            else
                right.emplace_back(1, q.x, q.y, q.z - s, q.i);
        }
        else
        {
            if (q.y < m)
            {
                add(q.x, q.z);
                left.emplace_back(0, q.x, q.y, q.z, 0);
            }
            else
                right.emplace_back(0, q.x, q.y, q.z, 0);
        }
    }
    for (auto &q : left)
        if (!q.q)
            add(q.x, -q.z);
    solve(left, l, m);
    solve(right, m, r);
}

int main()
{
    scanf("%d%d", &N, &Q);
    for (int i = 1; i <= N; ++i)
    {
        scanf("%d", A + i);
        B[M++] = A[i];
        op.emplace_back(0, i, A[i], 1, 0);
    }
    for (int i = 0; i < Q; ++i)
    {
        char c;
        scanf(" %c", &c);
        bool q = c == 'Q';
        if (q)
        {
            int l, r, k;
            scanf("%d%d%d", &l, &r, &k);
            op.emplace_back(1, l, r, k, i);
        }
        else
        {
            int x, y;
            scanf("%d%d", &x, &y);
            op.emplace_back(0, x, A[x], -1, 0);
            B[M++] = A[x] = y;
            op.emplace_back(0, x, A[x], 1, 0);
        }
    }
    sort(B, B + M);
    M = unique(B, B + M) - B;
    for (auto &q : op)
        if (!q.q)
            q.y = lower_bound(B, B + M, q.y) - B;
    memset(res, -1, sizeof(res));
    solve(op, 0, M);
    for (int i = 0; i < Q; ++i)
    {
        if (res[i] != -1)
            printf("%d\n", B[res[i]]);
    }
    return 0;
}
帮我完善下面这串代码#include <msp430.h> unsigned int seconds = 0; // 记录秒数 unsigned int minutes = 0; // 记录分钟数 unsigned int home_score = 0; // 主队得分 unsigned int guest_score = 0; // 客队得分 void main(void){ WDTCTL = WDTPW + WDTHOLD; // 关闭看门狗定时器 // 配置定时器A TA0CTL = TASSEL_2 + MC_1 + ID_3; // 选择SMCLK作为时钟源,以1:8的分频计数模式 TA0CCR0 = 62500; // 定时器计数到62500时产生中断,即1秒钟 TA0CCTL0 = CCIE; // 允许定时器A中断 // 配置按键中断 P1DIR &= ~(BIT1 + BIT2); // P1.1和P1.2作为输入 P1REN |= BIT1 + BIT2; // P1.1和P1.2启用上拉电阻 P1OUT |= BIT1 + BIT2; // P1.1和P1.2上拉 P1IE |= BIT1 + BIT2; // P1.1和P1.2开启中断 P1IES |= BIT1 + BIT2; // P1.1和P1.2设置为下降沿触发 P1IFG &= ~(BIT1 + BIT2); // 清除P1.1和P1.2的中断标志位 // 配置LED灯 P4DIR |= BIT7; // P4.7作为输出 __enable_interrupt(); // 开启全局中断 while(1) { // 显示计时器和得分 P4OUT |= BIT7; // 点亮LED灯 __delay_cycles(500000); // 延时0.5秒 P4OUT &= ~BIT7; // 熄灭LED灯 __delay_cycles(500000); // 延时0.5秒 } } #pragma vector=TIMER0_A0_VECTOR // 定时器A中断服务程序 __interrupt void Timer_A(void){ seconds++; // 秒数加1 if(seconds == 60) // 一分钟过去了 { seconds = 0; // 秒数清零 minutes++; // 分钟数加1 } if(minutes == 45) // 比赛结束 { TA0CTL = MC_0; // 停止定时器A } } #pragma vector=PORT1_VECTOR // 按键中断服务程序 __interrupt void Port_1(void){ if(P1IFG & BIT1) // P1.1的中断标志位被触发了 { home_score++; // 主队加分 } else if(P1IFG & BIT2) // P1.2的中断标志位被触发了 { guest_score++; // 客队加分 } P1IFG &= ~(BIT1 + BIT2); // 清除P1.1和P1.2的中断标志位} }
06-06
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值