[BZOJ]3744: Gty的妹子序列 分块+树状数组+主席树

22 篇文章 0 订阅
10 篇文章 0 订阅

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

题解:

分块+树状数组+主席树。用 ans[i][j] 表示第 i 块左端点到第j个点的逆序对数。对于询问 l,r ,可以快速得出右边大部分的答案,对于 l <script type="math/tex" id="MathJax-Element-279">l</script>到所在的块的右端点,可以用主席树求出区间内有多少个数比指定数小。

代码:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=50010;
int n,m,belong[maxn],l[250],r[250],a[maxn];
int ans[250][maxn];
struct Num{int x,id;}A[maxn];
bool cmp(Num a,Num b){return a.x<b.x;}
int s[maxn];
int lowbit(int x){return x&-x;}
void add(int x,int y){if(x<=0)return;for(int i=x;i<=n;i+=lowbit(i))s[i]+=y;}
int getsum(int x){int re=0;for(int i=x;i>0;i-=lowbit(i))re+=s[i];return re;}
int z=0,root[maxn],lc[maxn*30],rc[maxn*30],sum[maxn*30];
int q[maxn],hh;
void ins(int &u,int l,int r,int x)
{
    if(!u)u=++z;
    sum[u]++;
    if(l==r)return;
    int mid=l+r>>1;
    if(x<=mid)ins(lc[u],l,mid,x);
    else ins(rc[u],mid+1,r,x);
}
void merge(int &u1,int u2)
{
    if(!u1){u1=u2;return;}
    if(!u2)return;
    sum[u1]+=sum[u2];
    merge(lc[u1],lc[u2]);
    merge(rc[u1],rc[u2]);
}
int query(int u1,int u2,int l,int r,int ll,int rr)
{
    if(!u1&&!u2)return 0;
    if(l<=ll&&rr<=r)return sum[u2]-sum[u1];
    int mid=ll+rr>>1;
    if(r<=mid) return query(lc[u1],lc[u2],l,r,ll,mid);
    else if(l>mid) return query(rc[u1],rc[u2],l,r,mid+1,rr);
    else return query(lc[u1],lc[u2],l,mid,ll,mid)+query(rc[u1],rc[u2],mid+1,r,mid+1,rr);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&A[i].x),A[i].id=i;
    sort(A+1,A+1+n,cmp);
    int K=0;A[0].x=-1;
    for(int i=1;i<=n;i++)
    {
        if(A[i].x!=A[i-1].x)K++;
        a[A[i].id]=K;
    }
    int sq=(int)sqrt(n);
    for(int i=1;i<=n;i++)
    {
        belong[i]=(i-1)/sq+1;
        r[belong[i]]=i;
        if(!l[belong[i]])l[belong[i]]=i;
    }
    int t=0;
    for(int i=1;i<=belong[n];i++)
    {
        memset(s,0,sizeof(s));
        ans[i][l[i]]=0;add(a[l[i]],1);
        for(int j=l[i]+1;j<=n;j++)
        {
            ans[i][j]=ans[i][j-1]+(j-l[i]-getsum(a[j]));
            add(a[j],1);
        }
    }
    for(int i=1;i<=n;i++)ins(root[i],1,K,a[i]),merge(root[i],root[i-1]);
    scanf("%d",&m);
    memset(s,0,sizeof(s));
    int lastans=0;
    while(m--)
    {
        int L,R;
        scanf("%d%d",&L,&R);
        L^=lastans,R^=lastans;
        if(L>R)swap(L,R);
        int bl=belong[L],br=belong[R];
        if(bl==br)
        {
            lastans=0;hh=0;
            for(int i=R;i>=L;i--)
            {
                lastans+=getsum(a[i]-1);
                q[++hh]=a[i];add(a[i],1);
            }
            printf("%d\n",lastans);
            for(int i=1;i<=hh;i++)add(q[i],-1);
        }
        else
        {
            lastans=ans[bl+1][R];
            for(int i=L;i<=r[bl];i++)
            lastans+=query(root[i],root[R],1,a[i]-1,1,K);
            printf("%d\n",lastans);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值