【uva 12003 Array Transformer】【分块】

题意:

给定一个序列,要求查询一个区间内有多少个数小于v。并且把第p个位置的值改为u * k / (R - L + 1)。

思路:

单点修改和区间查询。
线段树的话还要套平衡树,不会写,也不想写(:зゝ∠)

还是分块搞吧。。

把n个数分成 sqrt(n)个区间,然后把每个区间内都排序。
对于查询的时候,对于完全覆盖的块直接二分查,对于没完全覆盖的两端,直接暴力扫。

修改的时候,对于原数据直接改,对于排序后的要注意存的时候存上id,这样找出p在哪一块,直接覆盖掉,但是覆盖掉可能比原来的值大或者小,那么就直接交换到合适的位置就好了。

代码:

#include <set>
#include <map>
#include <queue>
#include <vector>
#include <math.h>
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
using  namespace  std;

#define ff first
#define ss second
#define pb push_back
#define ll long long
#define mod 1000000007
#define ull unsigned long long
#define mst(ss,b) memset(ss,b,sizeof(ss));
#define pl(x) cout << #x << "= " << x << endl;
const int inf = 0x3f3f3f3f;
const int N = 3e5+5;

int n, m, u;
int num, blocks;
ll ans[N], a[N];
//num分块的个数
//blocks表示块的大小

struct node{
    ll v;
    int id;
    node(int a = 0, int b = 0) {
        v = a, id = b;
    }
    bool operator < (const node& rhs)const {
        return v<rhs.v;
    }
};

vector<node>q[1000];

int query(int L, int R, int v){
    int posL = (L-1)/blocks;
    int posR = (R-1)/blocks;
    if(posL == posR){
        int k = 0;
        for(int i=L; i<=R; i++)if(a[i] < v)k++;
        return k;
    }
    int k = 0;
    for(int i=L; i<=(posL+1)*blocks; i++)if(a[i] < v)k++;
    for(int i=posR*blocks+1; i<=R; i++)if(a[i] < v)k++;
    for(int i=posL+1; i<posR; i++)k += lower_bound(q[i].begin(), q[i].end(), v) - q[i].begin();
    return k;
}

void update(int p, ll v){
    a[p] = v;
    int cur = (p-1)/blocks;
    for(int i=0; i<q[cur].size(); i++){
        if(q[cur][i].id == p){
            if(q[cur][i].v == v)return ;
            q[cur][i].v = v;
            int pos = i;
            while(pos > 0 && q[cur][pos].v < q[cur][pos-1].v){
                swap(q[cur][pos], q[cur][pos-1]);
                pos--;
            }
            while(pos < q[cur].size()-1 && q[cur][pos].v > q[cur][pos+1].v){
                swap(q[cur][pos], q[cur][pos+1]);
                pos++;
            }
            return ;
        }
    }
}

int  main(){
    scanf("%d%d%d", &n, &m, &u);
    blocks = sqrt(n);
    for(int i=1; i<=n; i++){
        scanf("%lld", &a[i]);
        q[num].pb(node(a[i], i)); //
        if(i/blocks != num)num++;
    }
    if(num * blocks != n)num++;

    for(int i=0; i<num; i++)sort(q[i].begin(), q[i].end());

    while(m--){
        int L, R, v, p;
        scanf("%d%d%d%d", &L, &R, &v, &p);
        int k = query(L, R, v);
        update(p, 1LL*u*k/(R-L+1));
    }

    for(int i=0; i<num; i++)
        for(int j=0; j<q[i].size(); j++)ans[q[i][j].id] = q[i][j].v;

    for(int i=1; i<=n; i++)printf("%lld\n", ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值