码迷,mamicode.com
首页 > 其他好文 > 详细

BZOJ 2141 排队 树套树

时间:2015-05-27 13:56:58      阅读:112      评论:0      收藏:0      [点我收藏+]

标签:操作   方法   查询   输出   数组   

题意:链接

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

题解:

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

主要的解题思路就是第一次先把所有的点加进树套树,之后对于每一个值,查询他后面的区间有多少个值比他小,则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) ;
    }
}

BZOJ 2141 排队 树套树

标签:操作   方法   查询   输出   数组   

原文地址:http://blog.csdn.net/wzq_qwq/article/details/46045385

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!