UVALive 6860 Most Influential Pumpkin

题意:给你一个序列,每次操作可以使一段连续的序列加1,每次更新完后输出这个序列的中位数。

注意到中位数的大小只会增加,不会减小,并且每次操作后最多使中位数加1.

将原序列分块操作,若块在询问的【L,R】中,则使块的cnt++,这样只对询问区间的首尾单独操作即可。

每次更新后查找序列中小于等于当前中位数的个数,如果个数小于等于n/2.则中位数++。

查找小于等于中位数的个数时用lower_bound会超时,可以存一个计数器p表示更新前的个数,因为大部分的区间p值不会改变。(但是最坏复杂度大了很多= =)


代码:

#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define lson o<<1
#define rson o<<1|1
#define CLR(A, X) memset(A, X, sizeof(A))
using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
const double eps = 1e-10;
int dcmp(double x){if(fabs(x)<eps) return 0; return x<0?-1:1;}
const int N = 6e4+5;

struct node {
    int len, cnt, p;
    PII a[300];

    void clear() { len = cnt = p = 0; }
    void push_back(int x, int y) { a[len++] = {x, y}; }
    void my_sort() { sort(a, a+len); }

    void update(int L, int R) {
        for(int i = 0; i < len; i++) {
            if(a[i].se<=R && a[i].se>=L) {
                a[i].fi++;
            }
        }
        my_sort();
    }

    int sum(int x) {
        while(p>=0 && a[p].fi+cnt>x) p--;
        while(p+1<len && a[p+1].fi+cnt<=x) p++;
        return p+1;
    }
}b[300];

int a[N];

int main() {
    int n, q, x;
    while(~scanf("%d%d", &n, &q)) {
        if(!n && !q) continue;
        int len = sqrt(n+0.5), tot = n/len+!!(n%len);
        for(int i = 0; i < tot; i++) b[i].clear();
        for(int i = 0; i < n; i++) {
            scanf("%d", &a[i]);
            b[i/len].pb(a[i], i);
        }
        for(int i = 0; i < tot; i++) b[i].my_sort();
        sort(a, a+n);
        int Mid = a[n/2];
        while(q--) {
            int L, R;
            scanf("%d%d", &L, &R);
            L--, R--;
            for(int i = L/len+1; i < R/len; i++) b[i].cnt++;
            if(L/len == R/len) b[L/len].update(L, R);
            else {
                b[L/len].update(L, (L/len+1)*len-1);
                b[R/len].update((R/len)*len, R);
            }
            int tmp = 0;
            for(int i = 0; i < tot; i++) {
                tmp += b[i].sum(Mid);
            }
            if(tmp <= n/2) Mid++;
            printf("%d\n", Mid);
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值