网站首页 全球最实用的IT互联网站!

人工智能P2P分享Wind搜索发布信息网站地图标签大全

当前位置:诺佳网 > 软件工程 > 后端开发 > C++ >

P4168 [Violet] 蒲公英 (离散化+分块 在线查询区间众

时间:2026-03-08 22:52

人气:

作者:admin

标签:

导读:P4168 [Violet] 蒲公英 离散化分块 在线查询区间众数 由于a_i范围是1e9的,记录a_i出现的次数不方便直接用数组记录,但是一共有n个数,我们就可以把它们排序去重,把a_i映射为在n个数中排...

离散化+分块 在线查询区间众数

由于a_i范围是1e9的,记录a_i出现的次数不方便直接用数组记录,但是一共有n个数,我们就可以把它们排序去重,把a_i映射为在n个数中排第几,这样映射后的值域就小于n了,我们就能直接用数组记录了,这就是离散化
将长度为 n 的数组分块,每块长度为 B=sqrt(n)
比如[0,B),[B,2*b)...[k*B,n)
对于所查询的区间 [l ,r] 设l位于块bl, r位于块br,
|-------------------p1----------------p2---------------------|
| ------l-------------|-------------------|-----------r----------|

其中p1 = (bl+1)*B,p2 = br*B-1

该区间的众数只可能为在 $[l,p1) ∪ (p2,r]$内出现的数 和 块 $[bl+1,br-1]$的众数之间

因为在加[l,p1) ∪ (p2,r]内出现的数的时候才会改变 块 [bl+1,br-1]的众数

当bl和br位于同一个块或相邻块为了方便些代码就直接暴力复杂度最大$2*sqrt(n)$

否则就是在cnt数组中只维护[l,p1) ∪ (p2,r]内出现的数 和 块 [bl+1,br-1]的众数,这些数字最多也是sqrt(n)级别的,在遍历l->p_1和p_2->r用vis数组判断是否已经加了某个数在[p1,p2]中的出现次数

p1->p2所有数出现的次数用前缀和维护
这样总复杂度就是O(nlog(n)+(n+q)*sqrt(n))
ac代码如下:

#include<bits/stdc++.h>
using namespace std;

#define ull signed long long 
// #define int long long 



#define CINT \
// cin>>T;
void solve(){
    int n,q;cin>>n>>q;
    vector<int> a(n);
    for(int i = 0;i<n;i++){
        cin>>a[i];
    }
    vector<int> b = a;
    // 离散化
    sort(b.begin(),b.end());
    b.erase(unique(b.begin(),b.end()),b.end());
    for(int i = 0;i<n;i++){
        a[i] = lower_bound(b.begin(),b.end(),a[i])-b.begin();
    }
    
    int B = sqrt(n);
    // 前缀和,存前i块各个数字出现的次数
    vector<vector<int>> pre((n-1)/B+1,vector<int> (b.size()));
    for(int i = 0;i<n;i+=B){
        if(i!=0) pre[i/B] = pre[i/B-1];
        for(int j = i;j<min(n,i+B);j++){
            pre[i/B][a[j]]++;
        }
    }
    // f[i][j]表示第i块到第j块的众数
    vector<vector<int>> f((n-1)/B+1,vector<int> ((n-1)/B+1));
    vector<int> cnt(b.size()),vis(b.size());
    for(int i = 0;i<=(n-1)/B;i++){
        int p = 0,mx = 0;
        fill(cnt.begin(),cnt.end(),0);
        for(int j = i;j<=(n-1)/B;j++){
            for(int k = j*B;k<min(n,j*B+B);k++){
                cnt[a[k]]++;
                if(cnt[a[k]]>mx || (cnt[a[k]]==mx and a[k]<p)){
                    p = a[k];
                    mx = cnt[a[k]];
                }
            }
            f[i][j] = p;
        }
    }
    fill(cnt.begin(),cnt.end(),0);

    auto upd = [&](int num,int& p,int& mx,int bl,int br){
        if(!vis[num]){ //判断指定在中间区间的出现次数是否已经被加过
            vis[num] = 1;
            cnt[num] += pre[br-1][num]-pre[bl][num];
        }
        if(cnt[num]>mx || (cnt[num]==mx and num<p)){
            p = num;
            mx = cnt[num];
        }
    };

    int x = 0;
    while(q--){
        int l,r;cin>>l>>r;
        l = (l+x-1)%n+1;r = (r+x-1)%n+1;
        l--,r--;
        if(l>r) swap(l,r);
        int bl = l/B,br = r/B;
        int p = 0,mx = 0;
        if(br-bl<=1){ //区间长度小于2*sqrt(n)直接暴力
            for(int i = l;i<=r;i++){
                cnt[a[i]]++;
                if(cnt[a[i]]>mx || (cnt[a[i]]==mx and a[i]<p)){
                    p = a[i];
                    mx = cnt[a[i]];
                }
            }
            for(int i = l;i<=r;i++){
                cnt[a[i]] = 0;
            }
        }else{ 
            for(int i = l;i<(bl+1)*B;i++){
                cnt[a[i]]++;
                upd(a[i],p,mx,bl,br);
            }
            for(int i = br*B;i<=r;i++){
                cnt[a[i]]++;
                upd(a[i],p,mx,bl,br);
            }
            upd(f[bl+1][br-1],p,mx,bl,br);

            // 还原操作
            for(int i = l;i<(bl+1)*B;i++) cnt[a[i]] = vis[a[i]] = 0;
            for(int i = br*B;i<=r;i++) cnt[a[i]] = vis[a[i]] = 0;
            cnt[f[bl+1][br-1]] = vis[f[bl+1][br-1]] = 0;
        }
        x = b[p];
        cout<<b[p]<<"\n";
    }
}
signed main(){
    std::cin.tie(nullptr)->sync_with_stdio(false);
    int T = 1;
    CINT;
    while(T--) solve();
    return 0;
}
温馨提示:以上内容整理于网络,仅供参考,如果对您有帮助,留下您的阅读感言吧!
相关阅读
  • 指针空置类型-nullptr

    指针空置类型-nullptr

    先看一段代码: #include lt;iostreamgt; using namespace std; void func(char* p) { cout lt;lt; q...
  • 基于范围的for循环

    基于范围的for循环

    c11基于范围的for循环,语法: for (Type declaration : expression) { // 循环体 } 在上面的...
本类排行
相关标签
本类推荐

CPU | 内存 | 硬盘 | 显卡 | 显示器 | 主板 | 电源 | 键鼠 | 网站地图

Copyright © 2025-2035 诺佳网 版权所有 备案号:赣ICP备2025066733号
本站资料均来源互联网收集整理,作品版权归作者所有,如果侵犯了您的版权,请跟我们联系。

关注微信