bzoj3744 Gty的妹子序列

3744: Gty的妹子序列

Time Limit: 20 Sec   Memory Limit: 128 MB
Submit: 967   Solved: 293
[ Submit][ Status][ Discuss]

Description

我早已习惯你不在身边,
人间四月天 寂寞断了弦。
回望身后蓝天,
跟再见说再见……
某天,蒟蒻Autumn发现了从 Gty的妹子树(bzoj3720) 上掉落下来了许多妹子,他发现
她们排成了一个序列,每个妹子有一个美丽度。
Bakser神犇与他打算研究一下这个妹子序列,于是Bakser神犇问道:"你知道区间
[l,r]中妹子们美丽度的逆序对数吗?"
蒟蒻Autumn只会离线乱搞啊……但是Bakser神犇说道:"强制在线。"
请你帮助一下Autumn吧。
给定一个正整数序列a,对于每次询问,输出al...ar中的逆序对数,强制在线。

Input

第一行包括一个整数n(1<=n<=50000),表示数列a中的元素数。
第二行包括n个整数a1...an(ai>0,保证ai在int内)。
接下来一行包括一个整数m(1<=m<=50000),表示询问的个数。
接下来m行,每行包括2个整数l、r(1<=l<=r<=n),表示询问al...ar中的逆序
对数(若ai>aj且i<j,则为一个逆序对)。
l,r要分别异或上一次询问的答案(lastans),最开始时lastans=0。
保证涉及的所有数在int内。

Output

对每个询问,单独输出一行,表示al...ar中的逆序对数。

Sample Input

4
1 4 2 3
1
2 4

Sample Output

2

HINT

Source

By Autumn




分块+树状数组+可持久化线段树

如果是离线求区间逆序对数(bzoj3289),可以用莫队+树状数组解决,这道题强制在线显然不能这样做。

考虑分块,f[i][j]表示第i块的起始位置到第j个点的逆序对数,这个可以用树状数组在O(n*sqrt(n)*logn)的复杂度预处理。

然后对于每次询问,如果左右端点在同一块内,树状数组暴力计算。否则找到左端点后面第一个完整的块t,f[t][r]直接统计到答案里,前面剩余的部分,对于每一个位置i,我们要求出[i+1,r]中小于a[i]的数的个数,可以用主席树做。




#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define pa pair<int,int>
#define maxn 50005
#define maxm 1000005
using namespace std;
int n,m,tot,cnt,ans,block,T;
int a[maxn],s[maxn],num[maxn],rt[maxn],f[250][maxn];
int ls[maxm],rs[maxm],sz[maxm];
pa b[maxn];
inline int read()
{
    int x=0,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();}
    return x*f;
}
void add(int x,int y)
{
    for(;x<=tot;x+=(x&(-x))) s[x]+=y;
}
int sum(int x)
{
    int ret=0;
    for(;x;x-=(x&(-x))) ret+=s[x];
    return ret;
}
void insert(int x,int &y,int l,int r,int val)
{
    y=++cnt;sz[y]=sz[x]+1;
    if (l==r) return;
    ls[y]=ls[x];rs[y]=rs[x];
    int mid=(l+r)>>1;
    if (val<=mid) insert(ls[x],ls[y],l,mid,val);
    else insert(rs[x],rs[y],mid+1,r,val);
}
int getans(int x,int y,int l,int r,int L,int R)
{
    if (sz[x]==sz[y]) return 0;
    if (l==L&&r==R) return sz[y]-sz[x];
    int mid=(l+r)>>1;
    if (R<=mid) return getans(ls[x],ls[y],l,mid,L,R);
    else if (L>mid) return getans(rs[x],rs[y],mid+1,r,L,R);
    else return getans(ls[x],ls[y],l,mid,L,mid)+getans(rs[x],rs[y],mid+1,r,mid+1,R);
}
int query(int x,int y)
{
    int ret=0;
    if (num[x]==num[y])
    {
        memset(s,0,sizeof(s));
        F(i,x,y) ret+=sum(tot)-sum(a[i]),add(a[i],1);
        return ret;
    }
    ret=f[num[x]+1][y];
    F(i,x,num[x]*block) ret+=getans(rt[i],rt[y],1,tot,1,a[i]-1);
    return ret;
}
int main()
{
    n=read();
    block=round(sqrt(n));
    F(i,1,n) b[i].first=read(),b[i].second=i;
    sort(b+1,b+n+1);
    F(i,1,n)
    {
        if (i==1||b[i].first!=b[i-1].first) tot++;
        a[b[i].second]=tot;
    }
    F(i,1,n) insert(rt[i-1],rt[i],1,tot,a[i]);
    F(i,1,n) num[i]=(i-1)/block+1;
    F(i,1,num[n])
    {
        memset(s,0,sizeof(s));
        F(j,(i-1)*block+1,n)
        {
            f[i][j]=f[i][j-1]+sum(tot)-sum(a[j]);
            add(a[j],1);
        }
    }
    m=read();
    F(i,1,m)
    {
        int x=read()^ans,y=read()^ans;
        ans=query(x,y);
        printf("%d\n",ans);
    }
    return 0;
}


你可能感兴趣的:(树状数组,分块,bzoj,可持久化线段树)