BZOJ - 2141 排队(树状数组套treap||分块)

2141: 排队

Time Limit: 4 Sec   Memory Limit: 259 MB
Submit: 762   Solved: 299
[ Submit][ Status]

Description

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

Input

第一行为一个正整数n,表示小朋友的数量;第二行包含n个由空格分隔的正整数h1,h2,…,hn,依次表示初始队列中小朋友的身高;第三行为一个正整数m,表示交换操作的次数;以下m行每行包含两个正整数ai和bi¬,表示交换位置ai与位置bi的小朋友。

Output

输出文件共m行,第i行一个正整数表示交换操作i结束后,序列的杂乱程度。

Sample Input

【样例输入】
3
130 150 140
2
2 3
1 3

Sample Output

1
0
3
【样例说明】
未进行任何操作时,(2,3)满足条件;
操作1结束后,序列为130 140 150,不存在满足ihj的(i,j)对;
操作2结束后,序列为150 140 130,(1,2),(1,3),(2,3)共3对满足条件的(i,j)。
【数据规模和约定】
对于100%的数据,1≤m≤2*103,1≤n≤2*104,1≤hi≤109,ai≠bi,1≤ai,bi≤n。

HINT

题意:有M次操作,每次操作,交换位置x,y的数,然后输出逆序对数

思路:对于修改操作,(x,y),对于区间内小于a[x]的数逆序对数都少一,大于的都加一,区间内小于a[y]的都加一,大于的都减一

统计小于val的数的个数就用树套树

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int maxn=20010;
typedef long long LL;
int N,M,a[maxn];
int X[maxn];
int cnt,tot;
struct Node
{
    int ch[2];
    int r;//优先级
    int v;//值
    int s;
    int cnt;//自身重复次数
    void init(int val){v=val;ch[0]=ch[1]=0;s=cnt=1;r=rand();}
    int cmp(int x)const
    {
        if(x==v)return -1;
        return x<v?0:1;
    }

}tree[maxn*20];
void maintain(int x)
{
    tree[x].s=tree[x].cnt;
    tree[x].s+=tree[tree[x].ch[0]].s+tree[tree[x].ch[1]].s;
}
void rotate(int &o,int d)
{
    int k=tree[o].ch[d^1];
    tree[o].ch[d^1]=tree[k].ch[d];
    tree[k].ch[d]=o;
    maintain(o);
    maintain(k);
    o=k;
}
void insert(int &o,int x)
{
    if(!o)
    {
        o=++tot;
        tree[o].init(x);
    }
    else
    {
        if(x==tree[o].v)tree[o].cnt++;
        else
        {
            int d=(x<tree[o].v?0:1);
            insert(tree[o].ch[d],x);
            if(tree[tree[o].ch[d]].r>tree[o].r)
                rotate(o,d^1);
        }
    }
    maintain(o);
}
void remove(int &o,int x)
{
    if(!o)return;
    int d=tree[o].cmp(x);
    if(d==-1)
    {
        int u=o;
        if(tree[o].cnt>1)tree[o].cnt--;
        else if(tree[o].ch[0]&&tree[o].ch[1])
        {
            int d2=(tree[tree[o].ch[0]].r>tree[tree[o].ch[1]].r?1:0);
            rotate(o,d2);
            remove(tree[o].ch[d2],x);
        }
        else
        {
            if(!tree[o].ch[0])o=tree[o].ch[1];
            else o=tree[o].ch[0];
        }
    }
    else remove(tree[o].ch[d],x);
    if(o)maintain(o);
}
//返回最大值
int get_max(int o)
{
    while(tree[o].ch[0])o=tree[o].ch[0];
    return tree[o].v;
}
//返回最小值
int get_min(int o)
{
    while(tree[o].ch[1])o=tree[o].ch[1];
    return tree[o].v;
}
//返回val的前驱,如果没有的话返回y
//y的初值可赋成0,表示没有前驱
int get_pred(int o,int val,int y)
{
    if(!o)return y;
    if(tree[o].v<=val)//注意大于等于号
        return get_pred(tree[o].ch[1],val,tree[o].v);
    else return get_pred(tree[o].ch[0],val,y);
}
//返回val的后继,如果没有的话返回y
//y的初值可赋成0,表示没有后继
int get_succ(int o,int val,int y)
{
    if(!o)return y;
    if(tree[o].v>=val)return get_succ(tree[o].ch[0],val,tree[o].v);
    else return get_succ(tree[o].ch[1],val,y);
}
//返回第k大的元素的值
int get_kth(int o,int k)
{
    if(!o)return 0;
    if(k<=tree[tree[o].ch[0]].s)return get_kth(tree[o].ch[0],k);
    else if(k>tree[tree[o].ch[0]].s+tree[o].cnt)
        return get_kth(tree[o].ch[1],k-tree[tree[o].ch[0]].s-tree[o].cnt);
    return tree[o].v;
}
//返回val的排名
int get_rank(int o,int val)
{
    if(!o)return 0;
    int lsize=tree[tree[o].ch[0]].s;
    if(val<tree[o].v)
        return get_rank(tree[o].ch[0],val);
    else if(val>tree[o].v)
        return get_rank(tree[o].ch[1],val)+lsize+tree[o].cnt;
    return lsize+tree[o].cnt;
}
int tr[maxn];
void update(int x,int val)
{
    while(x<=N)
    {
        insert(tr[x],val);
        x+=(x&(-x));
    }
}
void erase(int x,int val)
{
    while(x<=N)
    {
        remove(tr[x],val);
        x+=(x&(-x));
    }
}
int getsum(int x,int val)
{
    int sum=0;
    while(x)
    {
        sum+=get_rank(tr[x],val);
        x-=(x&(-x));
    }
    return sum;
}
pair<int,int> cal(int l,int r,int val)
{
    int x=getsum(r,val)-getsum(l-1,val);//小于等于val的个数
    int y=getsum(r,val-1)-getsum(l-1,val-1);//小于等于val-1的个数
    return make_pair(x-y,y);
}
int main()
{
    while(scanf("%d",&N)!=EOF)
    {
        for(int i=1;i<=N;i++)
        {
            scanf("%d",&a[i]);
            X[i]=a[i];
        }
        sort(X+1,X+1+N);
        cnt=unique(X+1,X+N+1)-X-1;
        for(int i=1;i<=N;i++)
            a[i]=lower_bound(X+1,X+N+1,a[i])-X;
        LL ans=0;
        pair<int,int> tmp;
        tot=0;
        memset(tr,0,sizeof(tr));
        for(int i=1;i<=N;i++)
        {
            update(i,a[i]);
            tmp=cal(1,i-1,a[i]);
            ans+=i-1-tmp.first-tmp.second;
        }
        printf("%lld\n",ans);
        scanf("%d",&M);
        while(M--)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            if(a[x]==a[y])
            {
                printf("%lld\n",ans);
                continue;
            }
            if(x>y)swap(x,y);
            if(a[x]>a[y])ans--;
            else ans++;
            if(y-x>1)
            {
                tmp=cal(x+1,y-1,a[x]);
                ans+=y-x-1-tmp.first-tmp.second;
                ans-=tmp.second;


                tmp=cal(x+1,y-1,a[y]);
                ans+=tmp.second;
                ans-=y-x-1-tmp.first-tmp.second;
            }
            erase(x,a[x]);
            erase(y,a[y]);
            swap(a[x],a[y]);
            update(x,a[x]);
            update(y,a[y]);
            printf("%lld\n",ans);
        }
    }
    return 0;
}


分块写起来更简单一些:

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=20010;
const int maxm=200;

int a[maxn],X[maxn];
int N,M;
int block[maxm][maxn];
int size;
int tree[maxn];

void update(int x,int val)
{
    while(x<=N)
    {
        tree[x]+=val;
        x+=(x&(-x));
    }
}
int getsum(int l,int r)
{
    int sum=0;
    while(r)
    {
        sum+=tree[r];
        r-=(r&(-r));
    }
    return sum;
}
void init()
{
    size=sqrt(N);
    int num=0,j=0;
    for(int i=0;i<N;i++)
    {
        block[num][j]=a[i];
        if(++j==size)num++,j=0;
    }
    for(int i=0;i<num;i++)sort(block[i],block[i]+size);
    if(j)sort(block[num],block[num]+j);
}
LL query(int l,int r,int x,int flag)
{
    if(l>r)return 0;
    int x_pos=l/size;
    int y_pos=r/size;
    LL ans=0;
    if(x_pos==y_pos)
    {
        for(int i=l;i<=r;i++)
        {
            if(a[i]<x)ans++;
            if(a[i]==x&&flag)ans++;
        }
        return  ans;
    }
    for(int i=l;i<(x_pos+1)*size;i++)
    {
        if(a[i]<x)ans++;
        if(a[i]==x&&flag)ans++;
    }
    for(int i=(y_pos)*size;i<=r;i++)
    {
        if(a[i]<x)ans++;
        if(a[i]==x&&flag)ans++;
    }
    for(int i=x_pos+1;i<y_pos;i++)
    {
        if(flag)
            ans+=upper_bound(block[i],block[i]+size,x)-block[i];
        else
            ans+=lower_bound(block[i],block[i]+size,x)-block[i];
    }
    return ans;
}
void change(int x,int val)
{
    if(a[x]==val)return ;
    int x_pos=x/size;
    int pos=0;
    while(block[x_pos][pos]<a[x])pos++;
    block[x_pos][pos]=val;
    if(val<=a[x])
        while(pos>0&&block[x_pos][pos]<block[x_pos][pos-1])
            swap(block[x_pos][pos],block[x_pos][pos-1]),pos--;
    else
        while(pos<size-1&&block[x_pos][pos]>block[x_pos][pos+1])
            swap(block[x_pos][pos],block[x_pos][pos+1]),pos++;
    a[x]=val;
}
int main()
{
    while(scanf("%d",&N)!=EOF)
    {
        int cnt=0;
        for(int i=0;i<N;i++)
        {
            scanf("%d",&a[i]);
            X[++cnt]=a[i];
        }
        sort(X+1,X+cnt+1);
        cnt=unique(X+1,X+cnt+1)-X-1;
        memset(tree,0,sizeof(tree));
        LL ans=0;
        for(int i=0;i<N;i++)
        {
            int pos=lower_bound(X+1,X+cnt+1,a[i])-X;
            ans+=i-getsum(1,pos);
            update(pos,1);
        }
        printf("%lld\n",ans);
        init();
        scanf("%d",&M);
        while(M--)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            if(x>y)swap(x,y);
            x--,y--;
            if(a[x]==a[y]||x==y){printf("%d\n",ans);continue;}
            if(a[x]<a[y])ans++;
            else if(a[x]>a[y])ans--;
            ans+=query(x+1,y-1,a[y],0);
            ans-=y-x-1-query(x+1,y-1,a[y],1);

            ans+=y-x-1-query(x+1,y-1,a[x],1);
            ans-=query(x+1,y-1,a[x],0);
            int tmp=a[x];
            change(x,a[y]);change(y,tmp);
            printf("%lld\n",ans);
        }
    }
    return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值