UVA 11990 bit + cdq 分治

题意:动态逆序对

做法:对于删除操作可以当做逆向的添加操作 , 所以对于每个数字都有三个属性 (T,ID,Num)  所以这道题其实就是一个三维偏序问题 。 对于一个数 只需要 左边比它大的数 和右边比它小的数 ,我们可以用一个bit logn 进行操作。

三位偏序 可以用CDQ 减去一维 , 我们先把左边算出来 ,再把右边算出来,最后处理中间部分的。

对于中间的部分 用双指针 一个指向 L,MID(时间)。 一个指向MID+1,R(时间) 就可以消除T的影响 ,  同时左右两边满足 id 是递增的!


#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
struct node
{
    int t,num,id;
}a[maxn],b[maxn];
int n, m;
int pos[maxn];
ll ans[maxn];
int l[maxn],r[maxn],bit[maxn];

inline void updata(int pos,int val)
{
    for(int i=pos;i<=n;i+=(i&(-i)) ) bit[i]+=val;
}
inline int query(int pos)
{
    int ans=0;
    for(int i=pos;i;i-=(i&(-i))) ans+=bit[i];
    return ans;
}

inline void cdq(int begin,int end)
{
   if(begin >= end) return;
   int mid=(begin+end)>>1;
   int l1 = begin ,l2=mid+1,temp;

    for(int i=begin;i<=end;++i)
    {
        if(a[i].t<=mid) b[l1++]=a[i];
        else b[l2++]=a[i];
    }
    for(int i=begin;i<=end;++i) a[i]=b[i];
    temp=begin;
    for(int i=mid+1;i<=end;++i)
    {
        for( ; temp<=mid &&a[temp].id<a[i].id;temp++) updata(a[temp].num,1);
        l[a[i].t]+=(temp - begin - query(a[i].num));
    }
    for(int i=begin;i<=temp-1;i++) updata(a[i].num,-1);

    temp = mid;
    for(int i=end;i>=mid+1;--i)
    {
        for(; temp>=begin && a[temp].id >a[i].id ; temp--) updata(a[temp].num,1);
        r[a[i].t] += query(a[i].num-1);
    }
    for(int i=temp+1;i<=mid;i++) updata(a[i].num , -1);
    cdq(begin,mid); cdq(mid+1,end);
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(l,0,sizeof(l));
        memset(r,0,sizeof(r));
        memset(bit,0,sizeof(bit));
        memset(ans,0,sizeof(ans));
        for(int i=1;i<=n;++i)
        {
            scanf("%d",&a[i].num);
            a[i].id=i;  a[i].t=0;
            pos[a[i].num]=i;
        }
        int tt=n,x;
        for(int i=1;i<=m;++i)
        {
            scanf("%d",&x);
            a[pos[x]].t = tt--;
        }
        for(int i=1;i<=n;++i)
        {
            if(a[i].t==0) a[i].t=tt--;
        }
        cdq(1,n);
        for(int i=1;i<=n;++i) ans[i]=ans[i-1] + l[i] + r[i];
        for(int i=n;i>=n-m+1;--i) printf("%lld\n",ans[i]);
    }
    return 0;
}


























































 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值