BZOJ 2141 排队 树套树

题意:链接

方法:树套树(线段树+平衡树)(貌似树状数组+平衡树也可搞?)

题解:

首先这道题的题意是求区间逆序对,题中好像没说清?

主要的解题思路就是第一次先把所有的点加进树套树,之后对于每一个值,查询他后面的区间有多少个值比他小,则tot+=比他小的数的个数。

这样第一次输出就是初始的逆序对。

其次就是每次操作的时候,要讨论几种情况

1 . x==y||a[x]==a[y]的时候直接输出目前的逆序对数,因为如果交换的话会卡死,并且的确不改变。

2 . x>y的时候要交换x,y;

3 . 如果x+1!=y(等于的时候查也会死循环)则需要讨论四种情况:

情况一将x与y调换后,会增加[x+1,y-1]中比x大的数的个数个逆序对
情况二将x与y调换后,会减少[x+1,y-1]中比x小的数的个数个逆序对
情况三将x与y调换后,会减少[x+1,y-1]中比y大的数的个数个逆序对
情况四将x与y调换后,会增加[x+1,y-1]中比y小的数的个数个逆序对

4.最后再讨论x,y的关系,如果a[x]>a[y]则减少一个逆序对,反之增加。

5.输出目前的逆序对的个数,更新x与y位置的值。

后记:这题其实就用到了树套树的排名操作。

代码:

#include <map>//大爷给开的库不敢删
#include <set>//大爷给开的库不敢删
#include <cmath>//大爷给开的库不敢删
#include <queue>//大爷给开的库不敢删
#include <vector>//大爷给开的库不敢删
#include <cstdio>//大爷给开的库不敢删
#include <cstring>//大爷给开的库不敢删
#include <iomanip>//大爷给开的库不敢删
#include <iostream>//大爷给开的库不敢删
#include <algorithm>//大爷给开的库不敢删
#define N 320100
#define M 5000100
#define K 20010
#define INF 0x3f3f3f3f
using namespace std ;
struct node
{
    int l,r,w,v,size,rnd;
}tr[M];
int n,m,size,ans,tot;
int a[N],root[K];
void make_new(int k)
{
    tr[k].size = tr[tr[k].l].size + tr[tr[k].r].size + tr[k].w ;
}
void lturn(int &k)
{
    int t = tr[k].r ;
    tr[k].r = tr[t].l ;
    tr[t].l = k ;
    tr[t].size = tr[k].size ;
    make_new(k) ;
    k = t ;
}
void rturn(int &k)
{
    int t = tr[k].l ;
    tr[k].l = tr[t].r ;
    tr[t].r = k ;
    tr[t].size = tr[k].size ;
    make_new(k) ;
    k = t ;
}
void insert(int &k , int x)
{
    if(!k)
    {
        k = ++size ;
        tr[k].size = tr[k].w = 1 ;
        tr[k].v = x ;
        tr[k].rnd = rand() ;
        return ;
    }
    tr[k].size ++ ;
    if(tr[k].v==x){tr[k].w++;return;}
    if(x<tr[k].v){insert(tr[k].l,x);if(tr[tr[k].l].rnd<tr[k].rnd){rturn(k);}}
    else{insert(tr[k].r,x);if(tr[tr[k].r].rnd<tr[k].rnd){lturn(k);}}
}
void del(int &k , int x)
{
    if(tr[k].v==x)
    {
        if(tr[k].w>1){tr[k].w--;tr[k].size--;return;}
        if(tr[k].l*tr[k].r==0)k=tr[k].l+tr[k].r;
        else if(tr[tr[k].l].rnd<tr[tr[k].r].rnd){rturn(k);del(k,x);}
        else{lturn(k);del(k,x);}
    }
    else if(x<tr[k].v){del(tr[k].l,x);tr[k].size--;}
    else{del(tr[k].r,x);tr[k].size--;}
}
void build(int k,int l,int r,int x,int num)
{
    insert(root[k],num) ;
    if(l==r)return ;
    int mid = (l+r)>>1 ;
    if(x<=mid)build(k<<1,l,mid,x,num);
    else build(k<<1|1,mid+1,r,x,num);
}
void tr_rk_min(int k,int x)
{
    if(!k)return;
    if(tr[k].v==x){ans+=tr[tr[k].l].size;return;}
    else if(x<tr[k].v)tr_rk_min(tr[k].l,x);
    else{ans+=tr[tr[k].l].size+tr[k].w;tr_rk_min(tr[k].r,x);}
}
void a_rk_min(int k,int l,int r,int L,int R,int x)
{
    if(L==l&&R==r){tr_rk_min(root[k],x);return;}
    int mid=(l+r)>>1;
    if(mid>=R)a_rk_min(k<<1,l,mid,L,R,x);
    else if(mid<L)a_rk_min(k<<1|1,mid+1,r,L,R,x);
    else
    {
        a_rk_min(k<<1,l,mid,L,mid,x);
        a_rk_min(k<<1|1,mid+1,r,mid+1,R,x);
    }
}
void tr_rk_max(int k,int x)
{
    if(!k)return;
    if(tr[k].v==x){ans+=tr[tr[k].r].size;return;}
    else if(x<tr[k].v){ans+=tr[tr[k].r].size+tr[k].w;tr_rk_max(tr[k].l,x);}
    else{tr_rk_max(tr[k].r,x);}
}
void a_rk_max(int k,int l,int r,int L,int R,int x)
{
    if(L==l&&R==r){tr_rk_max(root[k],x);return;}
    int mid=(l+r)>>1;
    if(mid>=R)a_rk_max(k<<1,l,mid,L,R,x);
    else if(mid<L)a_rk_max(k<<1|1,mid+1,r,L,R,x);
    else
    {
        a_rk_max(k<<1,l,mid,L,mid,x);
        a_rk_max(k<<1|1,mid+1,r,mid+1,R,x);
    }
}
void update(int k,int l,int r,int x,int num,int y)
{
    del(root[k],y);
    insert(root[k],num);
    if(l==r)return;
    int mid=(l+r)>>1;
    if(x<=mid)update(k<<1,l,mid,x,num,y);
    else update(k<<1|1,mid+1,r,x,num,y);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)build(1,1,n,i,a[i]);

    for(int i=1;i<=n-1;i++)
    {
        ans=0;a_rk_min(1,1,n,i+1,n,a[i]);
        tot+=ans;
    }
    printf("%d\n",tot) ;
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        if(x==y||a[x]==a[y]){printf("%d\n",tot);continue;}
        if(x>y)swap(x,y);
        if(x+1!=y)
        {
            ans=0;a_rk_max(1,1,n,x+1,y-1,a[x]);tot+=ans;
            ans=0;a_rk_min(1,1,n,x+1,y-1,a[x]);tot-=ans;
            ans=0;a_rk_max(1,1,n,x+1,y-1,a[y]);tot-=ans;
            ans=0;a_rk_min(1,1,n,x+1,y-1,a[y]);tot+=ans;
        }
        if(a[x]<a[y])tot++;
        else tot--;
        update(1,1,n,x,a[y],a[x]);
        update(1,1,n,y,a[x],a[y]);
        swap(a[x],a[y]);
        printf("%d\n",tot) ;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值