[Cqoi2011]动态逆序对

24 篇文章 0 订阅
18 篇文章 1 订阅

主席树套树状数组。
主席树第一题。
链接
静态的逆序对问题很简单,用线段树或者是树状数组即可解决。
现在的问题是如何解决一道动态的逆序对问题?
我们先把所有的逆序对统计出来。
每次删除数,我们可以把这个数对于逆序对个数的贡献删除出去。
这个贡献如何统计呢?
Front[i]记录i位置之前有多少个数比这个数大
Back[i]记录i位置之后有多少个数比这个数小
每次删除的贡献就是这两个数之和
放屁!
如果之前我们已经删除过了一些数据,不就减少的多了?
如何统计呢?
把删除的数扔入主席树。
而查询前面多少个比这个数大的,和后面多少个数比这个小,主席树的基本功能!
需要注意的是。
本题要开long long。
本题也可以通过CDQ分治以及暴力分块的方法解决。
日后想起来在写!(主要是不会。)

#include <cstdio>
#include <iostream>
#include <cstring> 
#define il inline
#define lowbit(x) x&-x
#define ll long long
using namespace std;
const int maxm=1e6+100;
int root[maxm];
int sum[maxm<<5],lson[maxm<<5],rson[maxm<<5];
int front[maxm],back[maxm];
int tree[maxm],pos[maxm],val[maxm];
int A[maxm],B[maxm];
int n,m,size;
il ll ask(int x)
{
    ll ret=0;
    for(int i=x;i;i-=lowbit(i))
     ret+=tree[i];
    return ret;
}
il void up(int x)
{
    for(int i=x;i<=n;i+=lowbit(i)) tree[i]++; 
}
inline void updata(int &o,int l,int r,int valx)  
{  
    if (!o) o=++size;sum[o]++;  
    if (l==r) return;  
    int mid=(l+r)>>1;  
    if (valx<=mid) updata(lson[o],l,mid,valx);  
    else updata(rson[o],mid+1,r,valx);  
}  
il ll ask_big(int l,int r,int valx)
{
    if(l>r) return 0;
    l--,A[0]=B[0]=0;
    for(int i=l;i;i-=lowbit(i)) A[++A[0]]=root[i];
    for(int i=r;i;i-=lowbit(i)) B[++B[0]]=root[i];
    l=1,r=n;
    ll ans=0;
    while(l!=r)
    {
        int mid=(l+r)>>1;
        if(valx<=mid)
        {
            for(int i=1;i<=B[0];i++) ans+=(ll)sum[rson[B[i]]];
            for(int i=1;i<=A[0];i++) ans-=(ll)sum[rson[A[i]]];
            for(int i=1;i<=B[0];i++) B[i]=lson[B[i]];
            for(int i=1;i<=A[0];i++) A[i]=lson[A[i]];
            r=mid;
        }
        else
        {
            for(int i=1;i<=A[0];i++) A[i]=rson[A[i]];
            for(int i=1;i<=B[0];i++) B[i]=rson[B[i]];
            l=mid+1;
        }
    }
    return ans;
}
il ll ask_less(int l,int r,int valx)
{
    if(l>r) return 0;
    l--,A[0]=B[0]=0;
    for(int i=l;i;i-=lowbit(i)) A[++A[0]]=root[i];
    for(int i=r;i;i-=lowbit(i)) B[++B[0]]=root[i];
    l=1,r=n;
    ll ans=0;
    while(l!=r)
    {
        int mid=(l+r)>>1;
        if(valx>mid)
        {
            for(int i=1;i<=B[0];i++) ans+=(ll)sum[lson[B[i]]];
            for(int i=1;i<=A[0];i++) ans-=(ll)sum[lson[A[i]]];
            for(int i=1;i<=B[0];i++) B[i]=rson[B[i]];
            for(int i=1;i<=A[0];i++) A[i]=rson[A[i]];
            l=mid+1;
        }
        else
        {
            for(int i=1;i<=A[0];i++) A[i]=lson[A[i]];
            for(int i=1;i<=B[0];i++) B[i]=lson[B[i]];
            r=mid;
        }
    }
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    ll ans=0;
    for(int i=1;i<=n;i++) 
    {
        scanf("%d",&val[i]);
        pos[val[i]]=i;
        ans+=(ll)(front[i]=i-1-ask(val[i]));
        up(val[i]);
    }
    memset(tree,0,sizeof(tree));
    for(int i=n;i>=1;i--)
     back[i]=ask(val[i]-1),up(val[i]);
    //printf("%d\n",m);
    for(int i=1,del;i<=m;i++)
    {
        printf("%lld\n",ans);
        scanf("%d",&del);
        //printf("%d %d\n",front[pos[del]],back[pos[del]]);
        int poi=pos[del];
        ans-=((ll)front[poi]-ask_big(1,poi-1,del)+(ll)back[poi]-ask_less(poi+1,n,del));
        for(;poi<=n;poi+=lowbit(poi))
         updata(root[poi],1,n,del);
    }
    //printf("--%d--\n",ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值