【树状数组+线段树动态开点】BZOJ3295(Cqoi2011)[动态逆序对]题解

题目概述

给出一个 n 排列和 m 次删除操作,求每次删除操作前的逆序对数。

解题报告

直接树状数组+主席树就好了!树状数组+主席树就是修改和询问的时候有 log2n 个点,其他都一样。

然而你会RE or MLE,于是开始黑科技……因为这道题不需要历史版本,所以不用可持久化,但不可能和朴素做法一样建 n 棵线段树。不过我们可以一边修改一边新建,只有在某个节点被用到的时候才建出该节点,这样空间就比 O(nlog22n) 好很多了。

2017.10.14upd:其实这就是动态开点?!

示例程序

#include<cstdio>
using namespace std;
typedef long long LL;
const int maxn=100000,maxt=10000000;

int n,m,a[maxn+5],pos[maxn+5];LL ans=0;
struct node {node* son[2];int sum;};
typedef node* P_node;
node tem[maxt];P_node si=tem,ro[maxn+5];

void Insert(P_node &p,int pos,int k,int l=1,int r=n)
{
    if (pos<l||r<pos) return;p=p?p:si++;p->sum+=k;if (l==r) return;int mid=l+(r-l>>1);
    Insert(p->son[0],pos,k,l,mid);Insert(p->son[1],pos,k,mid+1,r);
}
int Query(P_node p,int L,int R,int l=1,int r=n)
{
    if (!p||R<l||r<L) return 0;if (L<=l&&r<=R) return p->sum;int mid=l+(r-l>>1);
    return Query(p->son[0],L,R,l,mid)+Query(p->son[1],L,R,mid+1,r);
}
inline void Update(int x,int num,int tem) {for (int i=x;i<=n;i+=i&-i) Insert(ro[i],num,tem);}
inline int Ask(int x,int L,int R) {int sum=0;for (int i=x;i;i-=i&-i) sum+=Query(ro[i],L,R);return sum;}
int main()
{
    freopen("program.in","r",stdin);
    freopen("program.out","w",stdout);
    scanf("%d%d",&n,&m);for (int i=1;i<=n;i++) ro[i]=0;
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);Update(pos[a[i]]=i,a[i],1);
        ans+=Ask(i-1,a[i]+1,n);
    }
    for (int i=1,x;i<=m;i++)
    {
        scanf("%d",&x);printf("%lld\n",ans);Update(pos[x],x,-1);
        ans-=Ask(pos[x]-1,x+1,n)+Ask(n,1,x-1)-Ask(pos[x],1,x-1);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值