洛谷 P1975 [国家集训队]排队

题意:给一个序列,每次交换\(a_l,a_r\),并且询问交换后的整个序列的逆序对数

分块+二分

刚开始的逆序对数可以直接用归并排序求出来,我们先更新答案再交换,考虑每次交换的\(a_l,a_r\),会影响逆序对的只可能是\([l,r]\)这个区间的数,如果单独拿出之间的一个数\(a_i(l+1\le i\le r-1)\)来说,对答案则有四种可能的影响

  1. \(a_i>a_l\to ans++\)

\(a_i\)\(a_l\)大,\(a_l\)\(a_r\)交换之后\(a_l\)下标比\(a_i\)大,逆序对数\(+1\)

  1. \(a_i<a_l\to ans--\)

\(a_i\)\(a_l\)小,已经是逆序对,\(a_l\)\(a_r\)交换,逆序对数\(-1\)

  1. \(a_i<a_r\to ans++\)

\(a_i\)\(a_r\)小,\(a_l\)\(a_r\)交换之后\(a_r\)下标比\(a_i\)小,逆序对数\(+1\)

  1. \(a_i>a_r\to ans--\)

\(a_i\)\(a_r\)大,已经是逆序对,\(a_l\)\(a_r\)交换,逆序对数\(-1\)

那么我们用分块来维护,对于在同一个块或相邻块的,暴力枚举\(a_i\)更新答案

然后考虑在每个块里维护这个块的有序序列,每次访问到这个块的时候,直接二分查找更新答案,边角的数直接枚举更新就好了

这样每次交换后对\(a_l\)\(a_r\)调整一下所在块的有序序列就好了

注意询问的\(l,r\)可能\(l>r\)所以要交换

蒟蒻也不会算块的大小什么的,常数还大的一批,就这样吧qwq

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#define N 20000
#define rep(i,s,t) for (register int i=s;i<=t;i++)
#define drep(i,s,t) for (register int i=s;i>=t;i--)
using namespace std;
int n,a[N+5],data[N+5],m,bs,blo[N+5],na[N+5],ans;
vector <int> p[N+5];
void merge_sort(int l,int r)
{
    if (r-l>0)
    {
        int it=l,mid=l+r>>1,p=l,q=mid+1;
        merge_sort(l,mid);
        merge_sort(mid+1,r);
        while (p<=mid||q<=r)
        {
            if (q>r||p<=mid&&na[p]<=na[q])
                data[it++]=na[p++];
            else
            {
                data[it++]=na[q++];
                ans+=mid-p+1;
            }
        }
        rep(i,l,r)
            na[i]=data[i];
    }
}
void com(int x,int l,int r)
{
    ans+=a[x]>a[l];
    ans-=a[x]<a[l];
    ans+=a[x]<a[r];
    ans-=a[x]>a[r];
}
void reset(int x)
{
    p[x].clear();
    rep(i,(x-1)*bs+1,x*bs)
        p[x].push_back(a[i]);
    sort(p[x].begin(),p[x].end());
}
void exc(int l,int r)
{
    swap(a[l],a[r]);
    reset(blo[l]);
    reset(blo[r]);
}
void calc(int l,int r)
{
    if (a[l]==a[r])
        return;
    ans+=(a[l]<a[r]);
    ans-=(a[l]>a[r]);
    if (blo[r]-blo[l]<=1)
    {
        rep(i,l+1,r-1)
            com(i,l,r);
    }
    else
    {
        rep(i,l+1,blo[l]*bs)
            com(i,l,r);
        rep(i,(blo[r]-1)*bs+1,r-1)
            com(i,l,r);
        rep(i,blo[l]+1,blo[r]-1)
        {
            ans+=p[i].end()-upper_bound(p[i].begin(),p[i].end(),a[l]);
            ans-=lower_bound(p[i].begin(),p[i].end(),a[l])-p[i].begin();
            ans+=lower_bound(p[i].begin(),p[i].end(),a[r])-p[i].begin();
            ans-=p[i].end()-upper_bound(p[i].begin(),p[i].end(),a[r]);
        }
    }
    exc(l,r);
}
int main()
{
    scanf("%d",&n);
    rep(i,1,n)
        scanf("%d",&a[i]),na[i]=data[i]=a[i];
    sort(data+1,data+n+1);
    bs=sqrt(n);    
    rep(i,1,n)
    {
        blo[i]=(i-1)/bs+1;        
        na[i]=a[i]=lower_bound(data+1,data+n+1,a[i])-data;
        p[blo[i]].push_back(a[i]);
    }
    rep(i,1,blo[n])
        sort(p[i].begin(),p[i].end());
    merge_sort(1,n);
    printf("%d\n",ans);
    scanf("%d",&m);
    int l,r;
    rep(i,1,m)
    {
        scanf("%d%d",&l,&r);
        if (l>r)
            swap(l,r);
        calc(l,r);
        printf("%d\n",ans);
    }
    return 0;
}
posted @ 2020-06-08 20:44  eee_hoho  阅读(145)  评论(0编辑  收藏  举报