【学习笔记】快乐分块

快乐分块

题外话

为什么分块算在树专题里(

引入


——luogu P1975 [国家集训队]排队

首先肯定是要统计出原来有多少个逆序对的

然后考虑如何统计交换后的答案改变

老师!我会暴力统计!

你退役罢(无慈悲

我直接给出结论(其实是我不会证明)

设交换的位置分别为(u,v),操作前逆序对个数为(ans)

容易得到:

对于(i in (u,v))

(h_i < h_v)(h_i > h_u),则ans++

(h_i > h_v)(h_i < h_u),则ans--

然后问题转化为统计((u,v))中有多少个数满足以上四种条件,就可以愉快树套树了

时间复杂度(O(nlog^2n))

可我不会树套树啊(

所以我们考虑对原数组分块

分块是什么

简而言之,分块是对数组进行分组,对于包含在区间内的整个组直接用较低时间复杂度的方法回答询问,剩下的则暴力统计

其中每一个整组叫做“块”

拿之前例题举例

假设没有修改操作

如果询问的((u,v))都是((1,n)),显然我们可以对整个数组进行排序,然后二分得到答案

假设我们已经对原数组进行了分块,那么对于每个在区间((u,v))里的整块,我们都可以对其进行排序,然后二分得到答案

对于剩下的非整块元素(一般称作边角),直接暴力统计

假设我们每(K)个元素分成一组,那么这么一趟的最坏时间复杂度为:

(O(K+frac{n}{K}logK))

显然当(K=sqrt{n})时时间复杂度最优,为(O(sqrt{n}+sqrt{n}logsqrt{n}))

感觉十分科学的时间复杂度(

现在,还有修改

我们原本的块是有序的

新交换进来的数,我们直接对新来的数插入排序即可

时间复杂度(O(K))

考虑到询问的复杂度,仍是当(K=sqrt{n})时时间复杂度最优

分块的优势

当你不会写树套树时骗分

对于树套树这种码量大又不好调的数据结构,我这种人在考场上基本不可能打出来

相比较而说,分块会相对好些很多

况且,对于树套树那巨大的常数,分块的小常数常常能让他跑得比复杂度更优的复杂度更快

还有呢

对于部分树套树难以统计的信息,分块常常能更好地统计

深刻理解请去刷ynOI

例题代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;

const int N=20010;

inline void read(int &x) {
    x=0;
    int f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9') {
        if (ch=='-') {
            f=-1;
        }
        ch=getchar();
    }
    while(ch>='0'&&ch<='9') {
        x=x*10+ch-'0';
        ch=getchar();
    }
    x*=f;
}

int ans;
int a[N];

namespace merge_sort {
int t[N];
inline void merge_sort(int l,int r) {
    if (r<=l+1) {
        return;
    }
    int mid=(l+r)>>1;
    int p=l,q=mid,cur=l;
    merge_sort(l,mid);
    merge_sort(mid,r);
    while(p<mid||q<r) {
        if (q>=r||(p<mid&&a[p]<=a[q])) {
            t[cur++]=a[p++];
        } else {
            t[cur++]=a[q++];
            ans+=(long long)mid-p;
        }
    }
    for(int i=l;i<r;i++) {
        a[i]=t[i];
    }
}
}

int n,m;
int siz;
int h[N];
int id[N],lft[N],rgh[N];
int k[N];

inline void change_ans(int x,int y,int i) {
    if (h[i]>h[x]) {
        ans++;
    }
    if (h[i]<h[x]) {
        ans--;
    }
    if (h[i]<h[y]) {
        ans++;
    }
    if (h[i]>h[y]) {
        ans--;
    }
}

void resort(int x,int y) {
    int L=id[x],R=id[y];
    int l=lower_bound(k+lft[L],k+rgh[L]+1,h[x])-k;
    int r=lower_bound(k+lft[R],k+rgh[R]+1,h[y])-k;
    k[l]=h[y];
    int tmp=l;
    while(tmp>lft[L]&&k[tmp]<k[tmp-1]) {
        swap(k[tmp],k[tmp-1]);
        tmp--;
    }
    while(tmp<rgh[L]&&k[tmp]>k[tmp+1]) {
        swap(k[tmp],k[tmp+1]);
        tmp++;
    }
    k[r]=h[x];
    tmp=r;
    while(tmp>lft[R]&&k[tmp]<k[tmp-1]) {
        swap(k[tmp],k[tmp-1]);
        tmp--;
    }
    while(tmp<rgh[R]&&k[tmp]>k[tmp+1]) {
        swap(k[tmp],k[tmp+1]);
        tmp++;
    }
    swap(h[x],h[y]);
    /*
    printf("k:");
    for(int i=1;i<=n;i++) {
        printf("%d ",k[i]);
    }
    printf("
h:");
    for(int i=1;i<=n;i++) {
        printf("%d ",h[i]);
    }
    */
}

int main() {
    read(n);
    siz=sqrt(n);
    for(int i=1;i<=n;i++) {
        read(h[i]);
        k[i]=a[i]=h[i];
        id[i]=(i-1)/siz+1;
    }
    for(int i=1;i<=n;i++) {
        if (!lft[id[i]]) {
            lft[id[i]]=i;
        }
        int j=n-i+1;
        if (!rgh[id[j]]) {
            rgh[id[j]]=j;
        }
    }
    for(int i=1;i<=id[n];i++) {
        sort(k+lft[i],k+rgh[i]+1);
    }
    merge_sort::merge_sort(1,n+1);
    printf("%d
",ans);
    read(m);
    for(int i=1;i<=m;i++) {
        int x,y;
        read(x),read(y);
        if (x>y) {
            swap(x,y);
        }
        if (h[x]==h[y]) {
            printf("%d
",ans);
            continue;
        }
        if (h[x]<h[y]) {
            ans++;
        } else {
            ans--;
        }
        if (id[x]==id[y]) {
            for(int i=x+1;i<y;i++) {
                change_ans(x,y,i);
            }
            printf("%d
",ans);
            resort(x,y);
            continue;
        }
        for(int i=x+1;i<=rgh[id[x]];i++) {
            change_ans(x,y,i);
        }
        for(int i=lft[id[y]];i<y;i++) {
            change_ans(x,y,i);
        }
        //统计以下:
        //有多少个数小于h[x]:-
        //有多少个数小于h[y]:+
        //有多少个数大于h[x]:+
        //有多少个数大于h[y]:-
        for(int i=id[x]+1;i<id[y];i++) {
            int o1,o2,o3,o4;
            o1=o2=o3=o4=0;
            if (k[lft[i]]<h[x]) {
                o1=lower_bound(k+lft[i],k+rgh[i]+1,h[x])-k;
                o1=o1-lft[i];
            }//h[x]-
            if (k[rgh[i]]>h[x]) {
                o2=upper_bound(k+lft[i],k+rgh[i]+1,h[x])-k;
                o2=rgh[i]+1-o2;
            }//h[x]+
            if (k[lft[i]]<h[y]) {
                o3=lower_bound(k+lft[i],k+rgh[i]+1,h[y])-k;
                o3=o3-lft[i];
            }//h[y]+
            if (k[rgh[i]]>h[y]) {
                o4=upper_bound(k+lft[i],k+rgh[i]+1,h[y])-k;
                o4=rgh[i]+1-o4;
            }//h[y]-;
            //printf("o:%d %d %d %d
",o1,o2,o3,o4);
            ans=ans-o1+o2+o3-o4;
        }
        printf("%d
",ans);
        resort(x,y);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/tt66ea-blog/p/12989474.html