分个块(我不排序)

        分块的思想其实就是空间换时间,对于完整区间在查询时可简便查询,而对于不完整的区域,直接暴力查找即可。故查询时间最多就是2*(block)+(完整区间的数量);而总的时间只需要加上预处理的时间即可。所以做题时这个总的时间就需要再权衡一下,如果题目中查询次数较多可以考虑将区间大小适当的减小,否则就设成sqrt(n)。

重点在于找到合适的存储方法。

1.只涉及区间加法 传送门

对于这个只有区间加法,对于完整的块,可以增设一个数组只用来存完整区间还需加的数,类似于线段树的lazy标记,所以对于一个[L,R],对其中的完整区间进行lazy标记,对于不完整的区间对直接对a[i]进行修改,同时对于一个块的数都存进vector里面,并排序,这样在查询完整区间时,可以用二分查找,同时查询时对于每一个都是C*C-lazy[当前块的编号].

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cmath>

using namespace std;
const int maxn = 1e5+10;

int a[maxn],pos[maxn];
int tag[maxn],n,block,sum;
vector<int>v[maxn];

void init(){

    block=sqrt(n);
    sum=n/block;
    if(n%block)
        sum++;
    for(int i = 1; i <= n ; i++){
        pos[i]=(i-1)/block+1;
        v[pos[i]].push_back(a[i]);
    }
    for(int i = 1; i <= sum ; i++){
        sort(v[i].begin(),v[i].end());
    }
}
void resort(int po){/对于不完整区间重新排序

    v[pos[po]].clear();
    for(int i = (pos[po]-1)*block+1; i <= min(pos[po]*block,n); i++){
        v[pos[po]].push_back(a[i]);
    }sort(v[pos[po]].begin(),v[pos[po]].end());

}
void update(int l,int r,int x){

    for(int i = l ; i <= min(pos[l]*block,r); i++){
        a[i]+=x;
    }resort(l);
    if(pos[l]!=pos[r]){
        for(int i = (pos[r]-1)*block+1; i <= r ; i++)
            a[i]+=x;
        resort(r);
    }
    for(int i = pos[l]+1; i <= pos[r]-1; i ++)
        tag[i]+=x;
}
int query(int l,int r,int x){

    int res=0;
    for(int i = l ; i <= min(pos[l]*block,r); i++)
        if(a[i]+tag[pos[i]] < x)
        res++;
    if(pos[l]!=pos[r]){
        for(int i = (pos[r]-1)*block+1 ; i <= r ; i++)
            if(a[i]+tag[pos[i]] < x)
            res++;
    }
    for(int i = pos[l]+1 ; i <= pos[r]-1; i ++){
        int tem=x-tag[i];
        res+=lower_bound(v[i].begin(),v[i].end(),tem)-v[i].begin();
    }return res;
}
int main()
{
    scanf("%d",&n);
    for(int i = 1; i <= n ; i++){
        scanf("%d",&a[i]);
    }
    init();
    for(int i = 0 ; i < n ; i++){
        int po,l,r,c;
        scanf("%d%d%d%d",&po,&l,&r,&c);{
            if(!po){
                update(l,r,c);
            }
            else if(po == 1){
                printf("%d\n",query(l,r,c*c));
            }
        }
    }
    return 0;
}

 2.区间最小众数  传送门

 对于一段[L,R]最小众数只可能出现在完整区间内或是非完整区间内,先把数都离散化,然后把相同的数的下标都存到一个vector里面,这时vector里面的是升序的。然后用一个f[i][j]数组表示从i号块到j号块的最小众数。所以查询时,对于每一可能的最大值我们都可以在其vector里面查询到在[L,R]里面出现的次数

设成block=sqrt(n)的时候超时了,此时查询次数为n比较大,所以把块设得小一点,设成200就可以了!!!

#include <iostream>
#include <map>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <algorithm>

#define fkl(x) ((x-1)*bk+1)
#define fkr(x) (x*bk)
using namespace std;
const int maxn = 1e5+10;

vector<int>v[maxn];
map<int,int>m;
int pos[maxn],vis[maxn];
int a[maxn],n,bk,cut[maxn],f[505][505],F[505][505];

void init(){

    for(int i = 1; i <= n ; i++){
        scanf("%d",&a[i]);
    }
    bk=200;
    for(int i = 1; i <= n ; i++){
        m[a[i]]=1;
        pos[i]=(i-1)/bk+1;
    }
    int k=1;
    for(map<int,int>::iterator it=m.begin();it!=m.end();it++){
        vis[k]=it->first;
        m[it->first]=k++;
    }
    for(int i = 1 ; i <= n ; i++){
        a[i]=m[a[i]];
        v[a[i]].push_back(i);
    }
}
void pre(int x){

    memset(cut,0,sizeof cut);
    int Max=0,po;
    for(int i = fkl(x) ; i <= n ; i++){
        int t=++cut[a[i]];
        if(t>Max||(t==Max&&a[i]<po)){
            Max=t,po=a[i];
        }
        f[x][pos[i]]=po;
    }
}
void build(){

    for(int i = 1; i <= pos[n] ; i++){
        pre(i);
    }
}
int research(int l,int r,int x){/查询在[l,r]内x出现了几次

    return upper_bound(v[x].begin(),v[x].end(),r)-lower_bound(v[x].begin(),v[x].end(),l);
}
int query(int l,int r){

    int po=f[pos[l]+1][pos[r]-1];
    int Max=research(l,r,po);
    for(int i = l ; i <= min(fkr(pos[l]),r) ; i++){
        int t=research(l,r,a[i]);
        if(t>Max||(t==Max&&a[i]<po)){
            Max=t,po=a[i];
        }
    }
    if(pos[l]!=pos[r]){
        for(int i = fkl(pos[r]); i <= r; i ++){
            int t=research(l,r,a[i]);
            if(t>Max||(t==Max&&a[i]<po)){
                Max=t,po=a[i];
            }
        }
    }
    return po;
}
int main()
{
    scanf("%d",&n);
    init();
    build();
    for(int i = 1; i <= n ; i++){
        int l,r;scanf("%d%d",&l,&r);
        printf("%d\n",vis[query(l,r)]);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值