BZOJ 3295 [Cqoi2011]动态逆序对 - 树状数组套主席树/树状数组套treap

首先预处理,对于一个输入的数列求逆序对个数利用树状数组nlogn即可解决。

然后考虑删点的问题。每次删点需要在总ans中删去此节点前比此数大的数的个数和之后此数小的个数。

对于当前的节点,如果需要定点查询一段区间中比一定值c大或小的点的个数,只需要在线段树(或平衡树)上操作即可。而对于这道题,需要讨论每个点,即每个点均需建立一棵线段树。每次查询之后需要在每棵线段树上进行修改操作,单次复杂度logn,每棵则为nlogn,m次则为O(mnlogn),很明显时间无法接受,空间也无法接受,于是可以用树状数组优化。

从另一个方面考虑:如果不考虑查询的是一个区间(即不具有最大或最小值的限定)树状数组通过lowbit记录一条删除的路径,路径上每个点-1,下次查询时沿路径累加即可修正ans;而如果有最大最小值的范围限定,则需要利用线段树(或平衡树)查询。每个节点均需要一棵树支持区间查询,由此进行树套树。


树状数组套主席树

Time:4100 ms

Memory:115672 kb

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#include<cstring>
  
using namespace std;
  
const int maxn=160005;
  
struct tree
{
    int lson,rson,val;
}t[maxn*60];
  
int n,m,cnt;
long long ans;
int f[maxn];
int c[maxn];
int s[maxn];
  
void build_init(int &ro,int l,int r)
{
    ro=++cnt;
    if(l==r)return;
    int mid=l+r>>1;
    build_init(t[ro].lson,l,mid);
    build_init(t[ro].rson,mid+1,r);
}
void build_seg(int &ro,int pos,int val,int l,int r)
{
    if(!ro)ro=++cnt;
    t[ro].val+=val;
    if(l==r)return;
    int mid=l+r>>1;
    if(pos<=mid)build_seg(t[ro].lson,pos,val,l,mid);
    else build_seg(t[ro].rson,pos,val,mid+1,r);
}
int query_seg(int ro,int maxi,int l,int r)
{
    if(r==maxi)return t[ro].val;
    int mid=l+r>>1;
    if(mid>=maxi)return query_seg(t[ro].lson,maxi,l,mid);
    return query_seg(t[ro].rson,maxi,mid+1,r)+t[t[ro].lson].val;
}
void update_tree(int x,int pos,int val)
{
    int tmp;
    for(int i=x;i<=n;i+=i&-x)
        build_seg(c[i],pos,val,1,n);
}
long long query_tree(int x,int maxi)
{
    long long res=0;
    for(int i=x;i;i-=i&-i)
        res+=query_seg(c[i],maxi,1,n);
    return res;
}
void update(int x)
{
    for(int i=x;i<=n;i+=i&-i)s[i]++;
}
int query(int x)
{
    int res=0;
    for(int i=x;i;i-=i&-i)res+=s[i];
    return res;
}
long long getans(int pos,int a)
{
    return query_tree(pos,n)-query_tree(pos,a)+query_tree(n,a)-query_tree(pos,a);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        int a;
        scanf("%d",&a);
        f[a]=i;
        update_tree(i,a,1);
        update(a);
        ans+=query(n)-query(a);
    }
     
    for(int i=1;i<=m;i++)
    {
        printf("%lld\n",ans);
        int a;
        scanf("%d",&a);
        update_tree(f[a],a,-1);
        ans-=getans(f[a],a);
    }
} 


树状数组套treap

Time:10376 ms

Memory:38152 kb

注意可以建立一个空节点null,将NULL替换成null避免因为访问空指针而RE。

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#include<cstring>
  
using namespace std;
  
const int maxn=160005;
 
struct node
{
    int rank,val,size;
    node *son[2];
     
    bool cmp(int val_)
    {
        return val<val_;
    }
     
}Tnull,*null=&Tnull;
  
int n,m,cnt;
long long ans;
int f[maxn];
node* c[maxn];
int s[maxn];
 
 
node* newnode(node *&o,int val_)
{
    o=new node;
    o->son[0]=o->son[1]=null;
    o->rank=rand();
    o->val=val_;
    o->size=1;
}
void maintain(node *o)
{
    o->size=1;
    if(o->son[0]!=null)o->size+=o->son[0]->size;
    if(o->son[1]!=null)o->size+=o->son[1]->size;
}
void rotate(node *&o,bool d)
{
    node *p=o->son[d];o->son[d]=p->son[d^1];p->son[d^1]=o;
    maintain(o);maintain(p);o=p;
}
void insert(node *&o,int val)
{
    if(o==null)o=newnode(o,val);
    else
    {
        bool d=o->cmp(val);
        insert(o->son[d],val);
        if(o->son[d]->rank > o->rank)
            rotate(o,d);
    }
    maintain(o);
}
void del(node *&o,int val)
{
    if(o->val==val)
    {
        node *p=o;
        if(o->son[0]!=null&&o->son[1]!=null)
        {
            bool d=o->son[0]->rank < o->son[1]->rank;
            rotate(o,d);
            del(o->son[d^1],val);
        }
        else
        {
            if(o->son[0]!=null)o=o->son[0];
            else o=o->son[1];
            delete p;
        }
    }
    else {
        bool d=o->cmp(val);
        del(o->son[d],val);
        }
    if(o!=null)maintain(o);
} 
int count(node *o,int maxi)
{
    if(o==null)return 0;
    if(maxi>o->val)return o->son[0]->size+1+count(o->son[1],maxi);
    return count(o->son[0],maxi)+(maxi==o->val);
}
void update_tree(int x,int pos)
{
    for(int i=x;i<=n;i+=i&-i)
    {
        if(!c[i])newnode(c[i],pos);
        else insert(c[i],pos);
    }
}
void delete_tree(int x,int pos)
{
    for(int i=x;i<=n;i+=i&-i)
        del(c[i],pos);
}
long long query_tree(int x,int maxi)
{
    long long res=0;
    for(int i=x;i;i-=i&-i)
        res+=count(c[i],maxi);
    return res;
}
void update(int x)
{
    for(int i=x;i<=n;i+=i&-i)s[i]++;
}
int query(int x)
{
    int res=0;
    for(int i=x;i;i-=i&-i)res+=s[i];
    return res;
}
long long getans(int pos,int a)
{
    return query_tree(pos,n)-query_tree(pos,a)+query_tree(n,a)-query_tree(pos,a);
}
int main()
{
    srand(20170901);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        int a;
        scanf("%d",&a);
        f[a]=i;
        update_tree(i,a);
        update(a);
        ans+=query(n)-query(a);
    }
    for(int i=1;i<=m;i++)
    {
        printf("%lld\n",ans);
        int a;
        scanf("%d",&a);
        delete_tree(f[a],a);
        ans-=getans(f[a],a);
    }
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值