洛谷P5047 [Ynoi2019 模拟赛] Yuno loves sqrt technology II(离线区间逆序对+莫队二次离线)

题目

给你一个长为n(1<=n<=1e5)的序列a(0<=ai<=1e9),

m(1<=m<=1e5)次询问,每次查询一个区间[l,r]的逆序对数,可离线。

思路来源

登录 - 洛谷

三道经典分块题的更优复杂度解法&[Ynoi2019模拟赛]题解 - 博客 - OldDriverTree的博客

值域分块入门__ChiFAN_的博客-CSDN博客

题解

莫队二次离线:

1. 普通的莫队求区间逆序对个数,是莫队+树状数组求逆序对,复杂度O(n\sqrt[]{n}logn)

插入/删除一个值v时,实际要求当前区间[l,r]内,比v大/小的数的个数①,用树状数组求

而①可以继续离线,转化为求[1,r]比v大/小的数的个数-[1,l-1]比v大/小的数的个数

共有O(n\sqrt[]{n})个莫队询问,O(n)个前缀离线修改,

所以,需要一个O(\sqrt[]{n})的插入,O(1)的查询的数据结构,这就是值域分块

a[i]表示i位置的值,普通的莫队/分块都是按i分块,也就是按位置分块

而值域分块:按值的出现次数分块

cnt[i]:i这个值出现的次数,每\sqrt[]{n}个出现次数分一块,

这样就能实现O(\sqrt[]{n})的插入,O(1)的查询了

维护块内的个数的前缀和/后缀和,块间的个数的前缀和/后缀和,

插入时修改O(\sqrt[]{n})个块+块内位置,查询O(1)查

复杂度O(n\sqrt[]{n}+q\sqrt[]{n})

代码

#include
#include
#include
#include
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]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].rask[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=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

你可能感兴趣的:(#,分块/定期重构/莫队,算法,c++,数据结构,莫队,二次离线)