[bzoj3744]Gty的妹子序列【分块】【主席树】

【题目链接】
  http://www.lydsy.com/JudgeOnline/problem.php?id=3744
【题解】
  分块套路题。
  首先可以用可持久化线段树在 O(logN) O ( l o g N ) 的时间内求出,一个点在一段区间内的逆序对数。
  其次可以在 O(T) O ( T ) 的时间内求出两个大小为 T T 的有序区间的相互的逆序对数(Two pointers)。
  那么就可以分块了,预处理处理出一个与块一段紧邻着它的块的逆序对数,O(N3/2),查询时两边暴力查询,中间统一计算。总复杂度: O(NNlogN+N3/2) O ( N N l o g N + N 3 / 2 ) ,显然这个复杂度不够平均,把块在设小一点可以获得更优的复杂度。
  

/* --------------
    user Vanisher
    problem bzoj-3744 
----------------*/
# include <bits/stdc++.h>
# define    ll      long long
# define    inf     0x3f3f3f3f
# define    N       50010
# define    K       1010
# define    L       1
# define    R       2147483647
# define    ui      unsigned int 
using namespace std;
ui read(){
    ui tmp=0, fh=1; char ch=getchar();
    while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
    while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
    return tmp*fh;
}
struct node{
    ui pl,pr,num;
}T[N*40];
ui sum[K][K],k[K][K],num,size[K],place,n,a[N],S,rt[N],sl[K],sr[K],p[N];
void pre(){
    for (int i=1; i<=num; i++)
        for (int j=sl[i]; j<sr[i]; j++)
            for (int t=j+1; t<=sr[i]; t++)
                if (a[j]>a[t]) sum[i][i]++;
    for (ui i=1; i<=num; i++)
        for (ui j=i+1; j<=num; j++){
            sum[i][j]=sum[i][j-1];
            for (ui pl=1, pr=1; pl<=size[i]; pl++){
                while (pr<=size[j]&&k[j][pr]<k[i][pl])    pr++;
                sum[i][j]=sum[i][j]+pr-1;
            }
        }
}
ui extend(ui x, ui las, ui l, ui r){
    ui now=++place;
    T[now].num=T[las].num+1;
    if (l==r) return now;
    ui mid=(l+r)/2;
    if (mid>=x){
        T[now].pl=extend(x,T[las].pl,l,mid);
        T[now].pr=T[las].pr;
    }
    else {
        T[now].pl=T[las].pl;
        T[now].pr=extend(x,T[las].pr,mid+1,r);
    }
    return now;
}
ui querymx(ui x, ui nowr, ui nowl, ui l, ui r){
    if (l>x) return T[nowr].num-T[nowl].num;
    if (r<=x) return 0;
    ui mid=(l+r)/2;
    return querymx(x,T[nowr].pl,T[nowl].pl,l,mid)+querymx(x,T[nowr].pr,T[nowl].pr,mid+1,r); 
}
ui querymn(ui x, ui nowr, ui nowl, ui l, ui r){
    if (r<x) return T[nowr].num-T[nowl].num;
    if (l>=x) return 0;
    ui mid=(l+r)/2;
    return querymn(x,T[nowr].pl,T[nowl].pl,l,mid)+querymn(x,T[nowr].pr,T[nowl].pr,mid+1,r); 
}
int main(){
    n=read(); S=(int)pow(n,0.4)+1;
    for (ui i=1; i<=n; i++)
        a[i]=read();
    for (ui i=1; i<=n; i++)
        rt[i]=extend(a[i],rt[i-1],L,R);

    sl[num=1]=1; ui now=0; 
    for (ui i=1; i<=n; i++){
        if (now==S){
            size[num]=now; sr[num]=i-1;
            sort(k[num]+1,k[num]+size[num]+1);
            num++; now=0;
            sl[num]=i;
        }
        k[num][++now]=a[i]; p[i]=num; 
    }
    size[num]=now; sr[num]=n;
    sort(k[num]+1,k[num]+size[num]+1);
    pre();

    ui m=read(), lastans=0;
    for (ui i=1; i<=m; i++){
        ui l=read()^lastans, r=read()^lastans; lastans=0;
        if (p[l]==p[r]){
            for (ui j=l; j<r; j++)
                lastans+=querymn(a[j],rt[r],rt[j],L,R);
        }
        else {
            for (ui j=l; j<=sr[p[l]]; j++)
                lastans+=querymn(a[j],rt[r],rt[j],L,R);
            for (ui j=sl[p[r]]; j<=r; j++)
                lastans+=querymx(a[j],rt[j-1],rt[sr[p[l]]],L,R);
            for (ui j=p[l]+1; j<=p[r]-1; j++)
                lastans+=sum[j][p[r]-1];
        }
        printf("%u\n",lastans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值