题解 - 谍战情报

题目描述

当然,您想想,耳机里除了沙沙声什么也没有,屏幕上一片空白,对作战命令和周围的战场态势一无所知,可不就是这种感觉嘛!这黑暗,压得人喘不过气来啊。 ——《全频段阻塞干扰》
你深知战场上情报优势不在自己的黑暗,于是早已提前制作了一个工具从机房的网络交换机上获取网络情报,它可以实时获取机房里每个人进行机惨的几率,精确到0.1%。你已经使用了空间压缩技术把机房战场转化成了一条序列,序列上的元素表示这个人进行机惨的几率,现在你想知道某一片战场的平静程度,即这个序列中区间[l,r]里有多少个人的机惨几率不大于某个数。同时因为是实时战场数据,每个人机惨的几率是会变化的,即会若干次修改一个人的几率。
简化题意:给定一个序列,每次求区间小于某数的数的个数,或把一个数改成另一个数。

输入

第一行两个数 n,m,表示机房地形转化成序列后的长度和操作个数。
第二行 n 个整数 pi ,表示位置为 i 的人机惨的概率为pi/1000。
接下来 m 行每行第一个为整数op,若op为1则后面有两个整数x,y,表示把x位置上的概率改成y/1000,为2则后面有三个整数l,r,y表示查询区间 [l,r] 里概率小于 y/1000的有几个位置。

输出

对每个op为2的询问输出一行一个整数表示答案。

样例

样例输入

10 10
4 8 4 7 8 8 7 2 1 8
1 1 4
1 10 3
1 7 8
2 6 7 4
2 5 9 3
1 2 1
2 1 9 4
1 5 4
2 2 5 7
2 6 7 3

样例输出

0
2
3
3
0

提示

对10%的数据保证n ≤1e3, m ≤1e3。
对另外20%的数据保证op=2。
对100%的数据保证n ≤1e5, m ≤1e4, p,y≤1e3, x,l,r∈[1,n]。

分析

分块、块状数组

块状数组
即把一个数组分为几个块,块内信息整体保存,
对于一次区间操作,对区间內部的整块进行整体的操作,对区间边缘的零散块单独暴力处理。
一般分为sqrt(n)个块,
总时间复杂度为sqrt(n)

将序列分成sqrt(n) 块,用 vector 存储每一块里的元素并排序。

若操作为修改,则将对应块的元素更改后重新排序;

若操作为查询,对于被完全覆盖的块,直接二分,零散块则直接暴力

实现

sq为分成的块的数量,

st表示第i个块的第一个元素的下标,ed表示第i个块最后一个元素的下标,

bel[j] = i表示第j个元素属于第i个块

void init(){
    int sq = sqrt(n);
 
    for(int i = 1;i <= sq;i++){
        st[i] = n / sq * (i - 1) + 1;
        ed[i] = n / sq * i;
    }
    ed[sq] = n;
 
    for(int i = 1;i <= sq;i++)
        for(int j = st[i];j <= ed[i];j++){
            bel[j] = i;
            s[i].push_back(a[j]);
        }
     
    for(int i = 1;i <= sq;i++) sort(s[i].begin(),s[i].end());
}

代码

#include<bits/stdc++.h>
 
using namespace std;
 
const int N = 1e5 + 10;
 
int n,m;
int a[N];
int st[N],ed[N],bel[N];
vector<int> s[N];
 
void init(){
    int sq = sqrt(n);
 
    for(int i = 1;i <= sq;i++){
        st[i] = n / sq * (i - 1) + 1;
        ed[i] = n / sq * i;
    }
    ed[sq] = n;
 
    for(int i = 1;i <= sq;i++)
        for(int j = st[i];j <= ed[i];j++){
            bel[j] = i;
            s[i].push_back(a[j]);
        }
     
    for(int i = 1;i <= sq;i++) sort(s[i].begin(),s[i].end());
}
 
void modify(int k,int x){
    int pos = bel[k];
    *lower_bound(s[pos].begin(),s[pos].end(),a[k]) = x;
    a[k] = x;
    sort(s[pos].begin(),s[pos].end());
}
 
int query(int l,int r,int x){
    int cnt = 0;
 
    if(bel[l] == bel[r]){
        for(int i = l;i <= r;i++)
            if(a[i] < x) cnt++;
    }else{
        for(int i = l;i <= ed[bel[l]];i++)
            if(a[i] < x) cnt++;
        for(int i = bel[l] + 1;i <= bel[r] - 1;i++)
            cnt += lower_bound(s[i].begin(),s[i].end(),x) - s[i].begin();
        for(int i = st[bel[r]];i <= r;i++)
            if(a[i] < x) cnt++;
    }
 
    return cnt;
}
 
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];
 
    init();
 
    int op,l,r,x,y;
    while(m--){
        cin >> op;
 
        if(op == 1){
            cin >> x >> y;
            modify(x,y);
        }else{
            cin >> l >> r >> y;
            cout << query(l,r,y) << '\n';
        }
    }
 
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值