数列分块入门 【LibreOJ 1-9】

数列分块入门 1

给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法和单点查询
模板题

///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<new>
#include<ctime>
#include<vector>
#define MT(a,b) memset(a,b,sizeof(a));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai=acos(-1.0);
const double E=2.718281828459;
const int INF=0x3f3f3f3f;

ll num[100005];
ll pos[100005];  ///每个数属于那个一个块
ll lazy[505];   ///每个区间的lazy
ll cnt;     ///一个块块的数量

void block(ll n)
{
    memset(lazy,0,sizeof(lazy));
    cnt=(ll)sqrt(n);
    for(int i=1;i<=n;i++)
        pos[i]=(i+cnt-1)/cnt;
}
void add(ll l,ll r,ll x)
{
    for(int i=l;i<=min(pos[l]*cnt,r);i++)
        num[i]+=x;
    if(pos[l]!=pos[r])
    {
        for(int i=(pos[r]-1)*cnt+1;i<=r;i++)
            num[i]+=x;
    }
    for(int i=pos[l]+1;i<=pos[r]-1;i++)
        lazy[i]+=x;
}
int main()
{
    ll n,op,l,r,x;
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",&num[i]);
    block(n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld %lld %lld %lld",&op,&l,&r,&x);
        if(op==0)
            add(l,r,x);
        else
            printf("%lld\n",num[r]+lazy[pos[r]]);
    }
    return 0;
}

数列分块入门 2

给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,询问区间内小于某个值 x 的元素个数。
思路:将每个块进行排序,将没有完全覆盖的块重新更新排序,完整的块利用lower_bound进行答案查找。

///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<new>
#include<ctime>
#include<vector>
#define MT(a,b) memset(a,b,sizeof(a));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai=acos(-1.0);
const double E=2.718281828459;
const int INF=0x3f3f3f3f;

ll n;
ll num[50005];
ll lazy[505];
ll pos[50005];
ll cnt;
vector<ll>q[505];

void block()
{
    memset(lazy,0,sizeof(lazy));
    cnt=(ll)sqrt(n);
    for(int i=1;i<=n;i++)
    {
        pos[i]=(i+cnt-1)/cnt;
        q[pos[i]].push_back(num[i]);
    }
    for(int i=1;i<=pos[n];i++)
        sort(q[i].begin(),q[i].end());
}

void update(int x)
{
    q[x].clear();
    for(int i=(x-1)*cnt+1;i<=min(cnt*x,n);i++)
        q[x].push_back(num[i]);
    sort(q[x].begin(),q[x].end());
}

void add(ll l,ll r,ll x)
{
    for(int i=l;i<=min(pos[l]*cnt,r);i++)
        num[i]+=x;
    update(pos[l]);
    if(pos[l]!=pos[r])
    {
        for(int i=(pos[r]-1)*cnt+1;i<=r;i++)
            num[i]+=x;
        update(pos[r]);
    }
    for(int i=pos[l]+1;i<=pos[r]-1;i++)
        lazy[i]+=x;
}
int query(ll l,ll r,ll x)
{
    int ans=0;
    for(int i=l;i<=min(pos[l]*cnt,r);i++)
    {
        if(num[i]+lazy[pos[l]]<x)
            ans++;
    }
    if(pos[l]!=pos[r])
    {
        for(int i=(pos[r]-1)*cnt+1;i<=r;i++)
            if(num[i]+lazy[pos[r]]<x)
                ans++;
    }
    for(int i=pos[l]+1;i<=pos[r]-1;i++)
    {
        ll s=x-lazy[i];
        int sub=lower_bound(q[i].begin(),q[i].end(),s)-q[i].begin();
        ans+=sub;
    }
    return ans;
}
int main()
{
    ll op,l,r,x;
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",&num[i]);
    block();
    for(int i=1;i<=n;i++)
    {
        scanf("%lld %lld %lld %lld",&op,&l,&r,&x);
        if(op==0)
            add(l,r,x);
        else
            printf("%d\n",query(l,r,x*x));
    }
}

数列分块入门 3

给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,询问区间内小于某个值 x 的前驱(比其小的最大元素)。
思路:和第二道题是一样的,注意题的范围和-1的情况即可

///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<new>
#include<ctime>
#include<vector>
#define MT(a,b) memset(a,b,sizeof(a));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai=acos(-1.0);
const double E=2.718281828459;
const int INF=0x3f3f3f3f;

ll n;
ll num[100005];
ll lazy[505];
ll pos[100005];
ll cnt;
vector<ll>q[505];

void block()
{
    memset(lazy,0,sizeof(lazy));
    cnt=(ll)sqrt(n);
    for(int i=1;i<=n;i++)
    {
        pos[i]=(i+cnt-1)/cnt;
        q[pos[i]].push_back(num[i]);
    }
    for(int i=1;i<=pos[n];i++)
        sort(q[i].begin(),q[i].end());
}

void update(int x)
{
    q[x].clear();
    for(int i=(x-1)*cnt+1;i<=min(cnt*x,n);i++)
        q[x].push_back(num[i]);
    sort(q[x].begin(),q[x].end());
}

void add(ll l,ll r,ll x)
{
    for(int i=l;i<=min(pos[l]*cnt,r);i++)
        num[i]+=x;
    update(pos[l]);
    if(pos[l]!=pos[r])
    {
        for(int i=(pos[r]-1)*cnt+1;i<=r;i++)
            num[i]+=x;
        update(pos[r]);
    }
    for(int i=pos[l]+1;i<=pos[r]-1;i++)
        lazy[i]+=x;
}
ll query(ll l,ll r,ll x)
{
    ll ans=-1;
    for(int i=l;i<=min(pos[l]*cnt,r);i++)
        if(num[i]+lazy[pos[l]]<x)
            ans=max(ans,num[i]+lazy[pos[l]]);
    if(pos[l]!=pos[r])
    {
        for(int i=(pos[r]-1)*cnt+1;i<=r;i++)
            if(num[i]+lazy[pos[r]]<x)
                ans=max(ans,num[i]+lazy[pos[r]]);
    }
    for(int i=pos[l]+1;i<=pos[r]-1;i++)
    {
        ll s=x-lazy[i];
        ll sub=lower_bound(q[i].begin(),q[i].end(),s)-q[i].begin();
        if(sub)
            ans=max(ans,q[i][sub-1]+lazy[i]);
    }
    return ans;
}
int main()
{
    ll op,l,r,x;
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",&num[i]);
    block();
    for(int i=1;i<=n;i++)
    {
        scanf("%lld %lld %lld %lld",&op,&l,&r,&x);
        if(op==0)
            add(l,r,x);
        else
            printf("%lld\n",query(l,r,x));
    }
}

数列分块入门 4

给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,区间求和(取模)
思路:模板题,注意会爆 int

///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<new>
#include<ctime>
#include<vector>
#define MT(a,b) memset(a,b,sizeof(a));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai=acos(-1.0);
const double E=2.718281828459;
const int INF=0x3f3f3f3f;

int n;
ll num[50005];
ll lazy[50005];
ll sum[50005];
int pos[50005];
int cnt;

void block()
{
    memset(lazy,0,sizeof(lazy));
    cnt=(int)sqrt(n);
    for(int i=1;i<=n;i++)
    {
        pos[i]=(i+cnt-1)/cnt;
        sum[pos[i]]+=num[i];
    }
}

void add(int l,int r,ll x)
{
    for(int i=l;i<=min(pos[l]*cnt,r);i++)
        num[i]+=x;
    sum[pos[l]]+=x*(min(pos[l]*cnt,r)-l+1);
    if(pos[l]!=pos[r])
    {
        for(int i=(pos[r]-1)*cnt+1;i<=r;i++)
            num[i]+=x;
        sum[pos[r]]+=x*(r-(pos[r]-1)*cnt);
    }
    for(int i=pos[l]+1;i<=pos[r]-1;i++)
        lazy[i]+=x;
}

ll query(int l,int r,int mod)
{
    ll ans=0;
    for(int i=l;i<=min(pos[l]*cnt,r);i++)
        ans=(ans+num[i]+lazy[pos[l]])%mod;
    if(pos[l]!=pos[r])
    {
        for(int i=(pos[r]-1)*cnt+1;i<=r;i++)
            ans=(ans+num[i]+lazy[pos[r]])%mod;
    }
    for(int i=pos[l]+1;i<=pos[r]-1;i++)
        ans=(ans+lazy[i]*cnt+sum[i])%mod;
    return ans;
}

int main()
{
    int op,l,r;
    ll x;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",&num[i]);
    block();
    for(int i=1;i<=n;i++)
    {
        scanf("%d %d %d %lld",&op,&l,&r,&x);
        if(op==0)
            add(l,r,x);
        else
            printf("%lld\n",query(l,r,x+1));
    }
    return 0;
}

数列分块入门 5

给出一个长为 n 的数列 ,以及 n 个操作,操作涉及区间开方,区间求和。
思路:因为是开方,所以一个数字最多进行5此操作就会变成 1,如果是0就会是零,我们用一个数组来记录一个块是否还可以开根,类似于剪枝的效果,如果还能开方,暴力就可以了

///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<new>
#include<ctime>
#include<vector>
#define MT(a,b) memset(a,b,sizeof(a));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai=acos(-1.0);
const double E=2.718281828459;
const int INF=0x3f3f3f3f3f3f;

ll n;
ll num[50005];
ll pos[50005];

ll sum[500];
ll vis[500];
ll cnt;

void block()
{
    memset(sum,0,sizeof(sum));
    memset(vis,0,sizeof(vis));
    cnt=sqrt(n);
    for(ll i=1; i<=n; i++)
    {
        pos[i]=(cnt+i-1)/cnt;
        sum[pos[i]]+=num[i];
    }
}
void update(ll l,ll r)
{
    for(ll i=l; i<=min(pos[l]*cnt,r); i++)
    {
        sum[pos[l]]-=num[i];
        num[i]=sqrt(num[i]);
        sum[pos[l]]+=num[i];
    }
    if(pos[l]!=pos[r])
    {
        for(ll i=cnt*(pos[r]-1)+1; i<=r; i++)
        {
            sum[pos[r]]-=num[i];
            num[i]=sqrt(num[i]);
            sum[pos[r]]+=num[i];
        }
    }
    for(ll i=pos[l]+1; i<=pos[r]-1; i++)
    {
        if(vis[i]==0)
        {
            vis[i]=1;
            sum[i]=0;
            for(ll j=(i-1)*cnt+1; j<=min(i*cnt,n); j++)
            {
                num[j]=sqrt(num[j]);
                sum[i]+=num[j];
                if(num[j]>1)
                    vis[i]=0;
            }
        }
    }
}

ll query(ll l,ll r)
{
    ll ans=0;
    for(ll i=l; i<=min(pos[l]*cnt,r); i++)
        ans+=num[i];
    if(pos[l]!=pos[r])
        for(ll i=(pos[r]-1)*cnt+1; i<=r; i++)
            ans+=num[i];
    for(ll i=pos[l]+1; i<=pos[r]-1; i++)
        ans+=sum[i];
    return ans;
}
int main()
{
    ll op,l,r,x;
    while(~scanf("%lld",&n))
    {
        for(ll i=1; i<=n; i++)
            scanf("%lld",&num[i]);
        block();
        for(ll i=1; i<=n; i++)
        {
            scanf("%lld %lld %lld %lld",&op,&l,&r,&x);
            if(op)
                printf("%lld\n",query(l,r));
            else
                update(l,r);
        }
    }
    return 0;
}

数列分块入门 6

给出一个长为 n 的数列,以及 n 个操作,操作涉及单点插入,单点询问,数据随机生成。
思路:我们可以用 vector 来进行块的维护,方便我们进行插入,为了维护复杂度,如果一个块包含的数量太大了,我们应该重新分块。
 

///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cmath>
#include <queue>
#include <set>
#include <stack>
#include <map>
#include <new>
#include <ctime>
#include <vector>

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai = acos(-1.0);
const double E = 2.718281828459;
const int INF = 0x3f3f3f3f3f3f;
const ll mod = 1e9 + 7;

int n;
int num[200005];
int cnt, up;    ///cnt 每个块的数量  up块的上限
vector<int> q[505];

void block() {
    cnt = sqrt(n);
    up = (n + cnt - 1) / cnt;
    for (int i = 1; i <= n; i++) {
        q[(i + cnt - 1) / cnt].push_back(num[i]);
    }
}

int x[200005], sign;

void reblock() {
    sign = 0;
    for (int i = 1; i <= up; i++) {
        for (int j = 0; j < q[i].size(); j++) {
            x[++sign] = q[i][j];
        }
        q[i].clear();
    }

    cnt = sqrt(sign);
    up = (sign + cnt - 1) / cnt;
    for (int i = 1; i <= sign; i++) {
        q[(i + cnt - 1) / cnt].push_back(x[i]);
    }
}

int main() {
    int op, l, r, x;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", &num[i]);
    block();
    for (int i = 1; i <= n; i++) {
        scanf("%d %d %d %d", &op, &l, &r, &x);
        if (op == 0) {
            int group, sub;
            for (int i = 1; i <= up; i++) {
                if (l > q[i].size())
                    l -= q[i].size();
                else {
                    group = i;
                    sub = l;
                    break;
                }
            }
            q[group].insert(q[group].begin() + sub - 1, r);
            if (q[group].size() > 7 * cnt)
                reblock();
        } else {
            int group, sub;
            for (int i = 1; i <= up; i++) {
                if (r > q[i].size())
                    r -= q[i].size();
                else {
                    group = i;
                    sub = r;
                    break;
                }
            }
            printf("%d\n", q[group][sub - 1]);
        }
    }
    return 0;
}

 数列分块入门 7

给出一个长为 n 的数列,以及 n 个操作,操作涉及区间乘法,区间加法,单点询问。
思路:
主要考虑如何维护加法和乘法标记。 
考虑一般情况,设任意块内中任意元素为x,所携带的加法标记为a,所携带的乘法标记为m。

区间加法:加数为a′,原先的值为mx+a,进行加法后的值为mx+a+a′,所以只需要更新加法标记,变为a+a′。
区间乘法:乘数为m′,原先的值为mx+a,进行乘法后的值为m′mx+am′,所以需要更新加法和乘法标记,乘法标记变为m∗m′,
加法标记变为a∗m′。
对于左右边界块内的部分,首先把所有标记下放,然后暴力更新数据值。
 

///#include <bits/stdc++.h>
///#include <unordered_map>
///#include <unordered_set>
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cmath>
#include <queue>
#include <set>
#include <stack>
#include <map>
#include <new>
#include <ctime>
#include <vector>

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai = acos(-1.0);
const double E = exp(1.0);
const int INF = 0x3f3f3f3f;
const ll mod = 10007;

int n;
ll num[100005];
int pos[100005], cnt;
ll lazy_add[505], lazy_mul[505];

inline void pushdown(int s) {
    if (lazy_mul[s] == 1 && lazy_add[s] == 0)
        return;
    for (int i = (s - 1) * cnt + 1; i <= min(s * cnt, n); ++i) {
        num[i] = ((num[i] * lazy_mul[s]) % mod + lazy_add[s]) % mod;
    }
    lazy_mul[s] = 1;
    lazy_add[s] = 0;
}

inline void add(int l, int r, ll x) {
    pushdown(pos[l]);
    for (int i = l; i <= min(r, cnt * pos[l]); ++i) {
        num[i] = (num[i] + x) % mod;
    }
    if (pos[l] != pos[r]) {
        pushdown(pos[r]);
        for (int i = (pos[r] - 1) * cnt + 1; i <= r; ++i) {
            num[i] = (num[i] + x) % mod;
        }
    }
    for (int i = pos[l] + 1; i <= pos[r] - 1; ++i) {
        lazy_add[i] = (lazy_add[i] + x) % mod;
    }
}

inline void mul(int l, int r, ll x) {
    pushdown(pos[l]);
    for (int i = l; i <= min(r, pos[l] * cnt); ++i) {
        num[i] = (num[i] * x) % mod;
    }
    if (pos[l] != pos[r]) {
        pushdown(pos[r]);
        for (int i = (pos[r] - 1) * cnt + 1; i <= r; ++i) {
            num[i] = (num[i] * x) % mod;
        }
    }
    for (int i = pos[l] + 1; i <= pos[r] - 1; ++i) {
        lazy_mul[i] = lazy_mul[i] * x % mod;
        lazy_add[i] = lazy_add[i] * x % mod;
    }
}

inline void init() {
    cnt = sqrt(n);
    for (int i = 1; i <= n; ++i) {
        pos[i] = (i - 1) / cnt + 1;
        lazy_mul[pos[i]] = 1;
        lazy_add[pos[i]] = 0;
    }
}

int main() {
    scanf("%d", &n);
    init();
    for (int i = 1; i <= n; ++i) {
        scanf("%lld", &num[i]);
    }
    int op, l, r;
    ll x;
    for (int i = 1; i <= n; ++i) {
        scanf("%d %d %d %lld", &op, &l, &r, &x);
        if (op == 0) {
            add(l, r, x);
        } else if (op == 1) {
            mul(l, r, x);
        } else {
            printf("%lld\n", ((num[r] * lazy_mul[pos[r]]) % mod + lazy_add[pos[r]]) % mod);
        }
    }
    return 0;
}

数列分块入门 8

给出一个长为 n 的数列,以及 n 个操作,操作涉及区间询问等于一个数 c 的元素,并将这个区间的所有元素改为 c 。

最开始的思路还以为和数列分块入门3类似,一看时间才500ms,绝对不能像3一样来处理。我们可以 lazy 标记一个块是否全部被赋值为同一个数,如果被赋值的数和查询的数相等,答案直接加上块的长度,如果没有被赋值过,即 lazy == -1。我们就暴力判断,然后给该块的 lazy 赋值,不完整的块我们暴力修改即可。

///#include <bits/stdc++.h>
///#include <unordered_map>
///#include <unordered_set>
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cmath>
#include <queue>
#include <set>
#include <stack>
#include <map>
#include <new>
#include <ctime>
#include <vector>

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai = acos(-1.0);
const double E = exp(1.0);
const int INF = 0x3f3f3f3f;

int n;
int cnt, num[100005], pos[100005];
int lazy[505];

inline void update(int s, int l, int r, int x) {
    for (int i = (s - 1) * cnt + 1; i <= min(s * cnt, n); i++) {
        if (i >= l && i <= r) {
            num[i] = x;
        } else {
            if (lazy[s] != -1)
                num[i] = lazy[s];
        }
    }
    lazy[s] = -1;
}

inline int query(int l, int r, int x) {

    int ans = 0;
    if (lazy[pos[l]] == x)
        ans = ans + min(r, pos[l] * cnt) - l + 1;
    if (lazy[pos[l]] == -1) {
        for (int i = l; i <= min(r, pos[l] * cnt); i++) {
            if (num[i] == x)
                ans++;
        }
    }
    update(pos[l], l, min(pos[l] * cnt, r), x);///块  变为x的区间  x
    if (pos[l] != pos[r]) {
        if (lazy[pos[r]] == x) {
            ans = ans + r - (pos[r] - 1) * cnt;
        }
        if (lazy[pos[r]] == -1) {
            for (int i = (pos[r] - 1) * cnt + 1; i <= r; i++) {
                if (num[i] == x)
                    ans++;
            }
        }
        update(pos[r], (pos[r] - 1) * cnt + 1, r, x);
    }
    for (int i = pos[l] + 1; i < pos[r]; i++) {
        if (lazy[i] == x)
            ans = ans + min(i * cnt, n) - (i - 1) * cnt;
        if (lazy[i] == -1) {
            for (int j = (i - 1) * cnt + 1; j <= min(i * cnt, n); j++) {
                if (num[j] == x)
                    ans++;
            }
        }
        lazy[i] = x;
    }
    return ans;
}

int main() {
    memset(lazy, -1, sizeof(lazy));///lazy 标记初始化
    int l, r, x;
    scanf("%d", &n);
    cnt = sqrt(n);      ///块的大小
    for (int i = 1; i <= n; i++) {
        pos[i] = (i + cnt - 1) / cnt;
        scanf("%d", &num[i]);
    }
    for (int i = 1; i <= n; i++) {
        scanf("%d %d %d", &l, &r, &x);
        printf("%d\n", query(l, r, x));
    }
    return 0;
}

数列分块入门 9

给出一个长为 n 的数列,以及 n 个操作,操作涉及询问区间的最小众数。

参考博客:https://blog.csdn.net/yiqzq/article/details/80672013

第一种写法代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
const int mod = 10007;
int n, block, belog[maxn], l, r, m;
int a[maxn], t[maxn], really[maxn];  // a是原数组,t是拷贝数组,really[i]=j,表示经过离散化的数字i的原数是j
int dp[2005][2005];  // dp[i][j]表示第i块到第j块的众数是多少(注意此题是经过离散化的众数)
vector<int> v[maxn];  //存的数字i的分别位于哪些位置
int num[maxn];
inline int read() {  //读入挂
    int ret = 0, c, f = 1;
    for (c = getchar(); !(isdigit(c) || c == '-'); c = getchar())
        ;
    if (c == '-')
        f = -1, c = getchar();
    for (; isdigit(c); c = getchar()) ret = ret * 10 + c - '0';
    if (f < 0)
        ret = -ret;
    return ret;
}
void init(int x) {    //预处理第i块到第j块的众数是几
    int Max_num = 0;  //当前众数的数量
    memset(num, 0, sizeof(num));
    int mode = 0;                                     //众数
    for (int i = (x - 1) * block + 1; i <= n; i++) {  //暴力预处理
        int t = belog[i];
        num[a[i]]++;
        if (num[a[i]] > Max_num) {
            Max_num = num[a[i]];
            mode = a[i];
        }
        if (Max_num == num[a[i]] && mode > a[i])
            mode = a[i];  //因为要求的是最小众数
        dp[x][t] = mode;  //表示第x块到第t块的众数
    }
}
int solve(int l, int r, int a) {  //查找a的出现次数
    vector<int>::iterator x = upper_bound(v[a].begin(), v[a].end(), r);
    /*
    用upper_bound()好处在于不用+1了,因为upper_bound()已经帮你加过1了
    这里用两个lower_bound(),然后下面改成x-y+1 是错的,应该是一些特殊范围导致的
    */
    vector<int>::iterator y = lower_bound(v[a].begin(), v[a].end(), l);
    return x - y;
}
int query(int l, int r) {
    int t1 = belog[l];
    int t2 = belog[r];
    int mode = dp[t1 + 1][t2 - 1];  //判断中间完整块的众数
    int Max_num = solve(l, r, mode);
    for (int i = l; i <= min(r, t1 * block); i++) {  //暴力左边不完整区间
        int t = solve(l, r, a[i]);                   // t是数量
        if (Max_num < t || (Max_num == t && mode > a[i])) {
            Max_num = t;
            mode = a[i];
        }
    }
    if (t1 != t2) {
        for (int i = (t2 - 1) * block + 1; i <= r; i++) {
            int t = solve(l, r, a[i]);
            if (Max_num < t || (Max_num == t && mode > a[i])) {
                Max_num = t;
                mode = a[i];
            }
        }
    }
    return mode;
}
int main() {
    n = read();
    block = 50;  //开50比sqrt(n)速度要块好多
    for (int i = 1; i <= n; i++) {
        a[i] = read();
        t[i] = a[i];
        belog[i] = (i - 1) / block + 1;
    }
    sort(t + 1, t + 1 + n);
    m = unique(t + 1, t + 1 + n) - t - 1;  //离散化
    for (int i = 1; i <= n; i++) {
        a[i] = lower_bound(t + 1, t + 1 + m, a[i]) - t;
    }
    for (int i = 1; i <= n; i++) {
        v[a[i]].push_back(i);
    }
    for (int i = 1; i <= belog[n]; i++) {
        init(i);
    }
    for (int i = 1; i <= n; i++) {
        l = read();
        r = read();
        printf("%d\n", t[query(l, r)]);
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值