Luogu P5047 [Ynoi2019 Simulation Competition] Yuno loves sqrt technology II (offline section reverse order + Mo team offline for the second time)

topic

Give you a sequence a(0<=ai<=1e9) of length n(1<=n<=1e5),

m(1<=m<=1e5) times of query, querying the reverse logarithm of an interval [l,r] each time, which can be offline.

source of ideas

Login - Luogu

A Better Complexity Solution for Three Classic Blocking Questions & [Ynoi2019 Simulation Competition] Solution- Blog- OldDriverTree's Blog

Getting Started with Value Domain Blocking_ChiFAN_'s Blog - CSDN Blog

answer

Mo team offline for the second time:

1. Ordinary Mo team to find the number of reversed pairs in the interval, it is Mo team + tree array to find the reversed pair, the complexityO(n\sqrt[]{n}logn)

When inserting/deleting a value v, it actually requires the number of numbers larger/smaller than v in the current interval [l,r]①, use a tree array to find

And ① can continue to be offline, which is transformed into finding the number of numbers [1, r] larger/smaller than v-[1,l-1] the number of numbers larger/smaller than v

There are O(n\sqrt[]{n})a total of mo team inquiries, O(n)prefix offline modification,

Therefore, one needs to O(\sqrt[]{n})be inserted, and the data structure of O(1) query is required. This is the value domain block

a[i] represents the value of the i position, and the ordinary team/block is divided into blocks according to i, that is, divided into blocks according to the position

And the value domain block : block by the number of occurrences of the value

cnt[i]: the number of occurrences of the value i, \sqrt[]{n}each occurrence is divided into one piece,

In this way, the insertion can be realized O(\sqrt[]{n}), and the query of O(1)

Maintain the prefix and / suffix sum of the number within the block, the prefix and / suffix sum of the number between blocks,

When inserting, modify O(\sqrt[]{n})a block + position in the block, query O(1) query

the complexityO(n\sqrt[]{n}+q\sqrt[]{n})

the code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int N=100005;
int n,m;ll a[N];
inline int lowbit(int x){return x&-x;}
ll c[N],sz=0;
int p[N];bool cmp(int x,int y){return a[x]<a[y];}
inline void add(int x,ll k){sz+=k;for(;x<=n;x+=lowbit(x))c[x]+=k;}
inline ll getsum(int x){ll ans=0;for(;x;x-=lowbit(x))ans+=c[x];return ans;}
ll L[N],R[N];//L[i]:1~i-1有多少个数大于a[i];R[i]:i+1~n有多少个数小于a[i] 

const int block=310;
struct ASK
{
    int l,r,p;
}ask[N];
inline bool mmp(ASK n1,ASK n2)
{
    if(n1.l/block!=n2.l/block)return n1.l<n2.l;
    if((n1.l/block)&1)return n2.r<n1.r;
    return n1.r<n2.r;
}
struct node{int l,r,p,op;};
vector<node>ls[N],rs[N];
int B,bl[N/block+5],br[N/block+5],w[N];
int s[N/block+5],C[N];

ll ret[N],ans[N];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        p[i]=i;
    }
    sort(p+1,p+n+1,cmp);
    ll tmp=a[p[1]];a[p[1]]=1;
    for(int i=2,t=1;i<=n;i++)
    {
        if(a[p[i]]!=tmp)t++;
        tmp=a[p[i]];a[p[i]]=t;
    }//离散化 

    for(int i=1;i<=n;i++)
    {
        L[i]=L[i-1]+sz-getsum(a[i]);
        add(a[i],1);//L[i]转前缀和 
    }//for(int i=1;i<=n;i++)printf("%lld ",L[i]);puts("");
    memset(c,0,sizeof(c));
    for(int i=n;i>=1;i--)
    {
        R[i]=R[i+1]+getsum(a[i]-1);
        add(a[i],1);//R[i]转后缀和 
    }//for(int i=1;i<=n;i++)printf("%lld ",R[i]);puts("");

    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&ask[i].l,&ask[i].r);
        if(ask[i].l>ask[i].r)swap(ask[i].l,ask[i].r);
        ask[i].p=i;
    }
    sort(ask+1,ask+m+1,mmp);//排序 
    ask[0]=(ASK){1,0,0};
    for(int i=1;i<=m;i++)//莫队二次离线 
    {//把莫队的移动离线下来 
        ret[i]=L[ask[i].r]-L[ask[i-1].r]+R[ask[i].l]-R[ask[i-1].l];
             if(ask[i].r>ask[i-1].r)rs[ask[i-1].l-1].push_back((node){ask[i-1].r+1,ask[i].r,i,-1});
        else if(ask[i].r<ask[i-1].r)rs[ask[i-1].l-1].push_back((node){ask[i].r+1,ask[i-1].r,i, 1});
             if(ask[i].l<ask[i-1].l)ls[ask[i  ].r+1].push_back((node){ask[i].l,ask[i-1].l-1,i,-1});
        else if(ask[i].l>ask[i-1].l)ls[ask[i  ].r+1].push_back((node){ask[i-1].l,ask[i].l-1,i, 1});
    }

    B=(n-1)/block+1;
    for(int i=1;i<=B;i++) 
    {
        bl[i]=br[i-1]+1;
        br[i]=br[i-1]+block;
    }br[B]=n;//值域分块 
    for(int i=1;i<=B;i++)for(int j=bl[i];j<=br[i];j++)w[j]=i;

    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<w[a[i]];j++)s[j]++;
        for(int j=bl[w[a[i]]];j<=a[i];j++)C[j]++;
        for(int j=0;j<rs[i].size();j++)
        {
            node t=rs[i][j];
            int l=t.l,r=t.r;
            tmp=0;
            for(int k=l;k<=r;k++)tmp+=s[w[a[k]+1]]+C[a[k]+1];
            ret[t.p]+=t.op*tmp;
        }
    }
    memset(C,0,sizeof(C));
    memset(s,0,sizeof(s));
    for(int i=n;i>=1;i--)
    {
        for(int j=w[a[i]]+1;j<=B;j++)s[j]++;
        for(int j=a[i];j<=br[w[a[i]]];j++)C[j]++;
        for(int j=0;j<ls[i].size();j++)
        {
            node t=ls[i][j];
            int l=t.l,r=t.r;
            tmp=0;
            for(int k=l;k<=r;k++)tmp+=s[w[a[k]-1]]+C[a[k]-1];
            ret[t.p]+=t.op*tmp;
        }
    }

    for(int i=1;i<=m;i++)
    {
        ret[i]+=ret[i-1];//ret最终的值是差分值 
        ans[ask[i].p]=ret[i];
    }
    for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
    return 0;
}

Guess you like

Origin blog.csdn.net/Code92007/article/details/130550996