[bzoj 2141]排队

124 篇文章 2 订阅
7 篇文章 0 订阅

排排坐,吃果果,生果甜嗦嗦,大家笑呵呵。你一个,我一个,大的分给你,小的留给我,吃完果果唱支歌,大家乐和和。
红星幼儿园的小朋友们排起了长长地队伍,准备吃果果。不过因为小朋友们的身高有所区别,排成的队伍高低错乱,极不美观。设第i个小朋友的身高为hi,我们定义一个序列的杂乱程度为:满足i< j且hi>hj的(i,j)数量。
幼儿园阿姨每次会选出两个小朋友,交换他们的位置,请你帮忙计算出每次交换后,序列的杂乱程度。为方便幼儿园阿姨统计,在未进行任何交换操作时,你也应该输出该序列的杂乱程度。

这道题一开始先离散化,用树状数组求逆序对(本蒟蒻不会归并排序),之后我们发现交换两个位置l,r,其实对它的答案的影响只关两个位置之间的数,xjb乱想,发现答案跟区间的值跟a[l]和a[r]的大小关系有关,那看到这个,便想到了分块2,那这题就解决了,注意交换之后记得维护就好了。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
using namespace std;
struct node
{
    int s,d;
}wy[20010];
bool cmp(node a,node b)
{
    if(a.s>b.s)return false;
    if(a.s<b.s)return true;
    return 0;
}
int n,ans,a[20010],b[20010],s[20010],belong[20010],bl[20010],br[20010];
void ps(int x)
{
    int l=bl[x],r=br[x];
    for(int i=l;i<=r;i++)b[i]=a[i];
    sort(b+l,b+r+1);
}
void fk()
{
    int cnt=sqrt(n);
    for(int i=1;i<=n;i++)
    {
        int bg=(i-1)/cnt+1;
        belong[i]=bg;
        if(bl[bg]==0)bl[bg]=i,br[bg-1]=i-1;
    }
    br[belong[n]]=n;
}
int fup(int x,int k)
{
    int l=bl[x],r=br[x],ans=br[x]+1;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(b[mid]>k)ans=mid,r=mid-1;
        else l=mid+1;
    }
    return br[x]-ans+1;
}  
int fdown(int x,int k)
{
    int l=bl[x],r=br[x],ans=bl[x]-1;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(b[mid]<k)ans=mid,l=mid+1;
        else r=mid-1;
    }
    return ans-bl[x]+1;
}
int lowbit(int x){return x&-x;}
void add(int x,int k)
{
    while(x<=n)
    {
        s[x]+=k;
        x+=lowbit(x);
    }
}
int getsum(int x)
{
    int ans=0;
    while(x>=1)
    {
        ans+=s[x];
        x-=lowbit(x);
    }
    return ans;
}
inline void walk(int now,int x,int y)
{
    if(a[now]>a[x])ans++;
    if(a[now]<a[x])ans--;
    if(a[now]>a[y])ans--;
    if(a[now]<a[y])ans++;
}
void solve(int x,int y)
{
    int bx=belong[x],by=belong[y];
    if(a[x]<a[y])ans++;
    if(a[x]>a[y])ans--;
    if(bx==by)for(int i=x+1;i<y;i++)walk(i,x,y);
    else
    {
        for(int i=x+1;i<=br[bx];i++)walk(i,x,y);
        for(int i=bl[by];i<y;i++)walk(i,x,y);
        for(int i=bx+1;i<by;i++)
        {
            ans+=fup(i,a[x])+fdown(i,a[y]);
            ans-=fdown(i,a[x])+fup(i,a[y]);
        }
    }
}   
int main()
{
    //freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    int t,id=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&wy[i].s),wy[i].d=i;
    sort(wy+1,wy+n+1,cmp);wy[0].s=0;
    for(int i=1;i<=n;i++)
    {
        if(wy[i].s!=wy[i-1].s)id++;
        a[wy[i].d]=id;
    }
    for(int i=1;i<=n;i++)ans+=(i-1)-getsum(a[i]),add(a[i],1);
    printf("%d\n",ans);
    fk();
    for(int i=1;i<=belong[n];i++)ps(i);
    scanf("%d",&t);
    while(t--)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        if(x>y)swap(x,y);    
        solve(x,y);
        swap(a[x],a[y]);
        if(belong[x]==belong[y])ps(belong[x]);
        else ps(belong[x]),ps(belong[y]);
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值