洛谷 P3157 [CQOI2011]动态逆序对 题解

103 阅读2分钟

题目链接

思路

做法是树状数组套主席树。
考虑每次删除一个元素对答案的影响。删除位于第ii个位置的元素xx(指数字大小)后,答案减少的数量是位置小于ii且比xx大的数字的个数(记为firifir_i)以及位置大于ii且比xx小的数字的个数(记为secisec_i)。那么每次删除答案都要减少firi+secifir_i+sec_i
但是这样并没有考虑到之前已经删除过的元素,因此我们还要在此基础上把多减去的那些再加回来(也就是某个元素在之前已经被删除了,但是在删除当前元素的时候仍然计算了它俩的逆序对)。
也就是说我们要在所有删除的元素中找到位置在[1,i1][1,i-1]且比xx大的元素的个数,以及位置在[i+1,n][i+1,n]之间且比xx小的元素的个数。如果这些元素一直不变,显然可以通过建一棵主席树来做。但是在这道题中每次删除都会向这个序列中插入一个值,因此每次都要重新建一颗主席树,复杂度太高。
主席树维护的其实就是前缀和(或者说基于前缀的某些信息),这道题又是单点修改和区间查询。所以可以用树状数组的思想来优化,树状数组每个节点存一颗权值线段树(动态开点),线段树维护区间和,这样每次对删除序列进行修改就变成O(log2n)O(\log^2 n),总的复杂度优化为O(mlog2n)O(m\log^2n)
线段树的内存池要开6060倍。

代码

#include<bits/stdc++.h>
#define lb(x) (x&-x)
#define rep(i,st,ed) for(int i=st;i<=ed;++i)
#define bl(u,i) for(int i=head[u];i;i=e[i].nxt)
#define LLM LONG_LONG_MAX
#define LLm LONG_LONG_MIN
#define pii pair<ll,ll> 
typedef long long ll;
typedef double db;
using namespace std;
const ll INF=0x3f3f3f3f;
void read() {}
void say() {}
void say_() {}
template <typename T, typename... T2>
inline void read(T &_, T2 &... oth)
{
    int __=0;
    _=0;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            __=1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        _=_*10+ch-48;
        ch=getchar();
    }
    _=__?-_:_;
    read(oth...);
}
template <typename T>
void Out(T _)
{
    if(_<0)
    {
        putchar('-');
        _=-_;
    }
    if(_>=10)
       Out(_/10);
    putchar(_%10+'0');
}
template <typename T, typename... T2>
inline void say(T _, T2... oth)
{
	Out(_);
	putchar('\n');
	say(oth...);
}
template <typename T, typename... T2>
inline void say_(T _, T2... oth)
{
	Out(_);
	putchar(' ');
	say_(oth...);
}
/*#################################*/
const ll N=1E5+10;
ll n,q,ans,cnt;
ll a[N],root[N],BIT[N],fir[N],sec[N],loc[N];
struct Node{
	ll l,r,val;
}t[60*N];
void modify(ll l,ll r,ll &cur,ll pos)
{
	if(!cur)
		cur=++cnt;
	++t[cur].val;
	if(l==r)
		return;
	ll mid=(l+r)>>1;
	if(pos<=mid)
		modify(l,mid,t[cur].l,pos);
	else
		modify(mid+1,r,t[cur].r,pos);
}
ll query(ll l,ll r,ll cur,ll al,ll ar)
{
	if(al<=l && ar>=r)
		return t[cur].val;
	ll mid=(l+r)>>1;
	ll ret=0;
	if(al<=mid)
		ret+=query(l,mid,t[cur].l,al,ar);
	if(ar>mid)
		ret+=query(mid+1,r,t[cur].r,al,ar);
	return ret;
}
ll find(ll L,ll R,ll l,ll r)
{
	if(l>r)
		return 0;
	ll ret=0;
	while(L)
	{
		ret-=query(1,n,root[L],l,r);
		L-=lb(L);
	}
	while(R)
	{
		ret+=query(1,n,root[R],l,r);
		R-=lb(R);
	}
	return ret;
}
ll SUM(ll x)
{
	ll ret=0;
	while(x)
	{
		ret+=BIT[x];
		x-=lb(x);
	}
	return ret;
}
void ADD(ll x,ll val)
{
	while(x<=n)
	{
		BIT[x]+=val;
		x+=lb(x);
	}
}
int main()
{
	read(n,q);
	rep(i,1,n)
	{
		read(a[i]);
		loc[a[i]]=i;
	}
	rep(i,1,n)
	{
		fir[i]=SUM(n)-SUM(a[i]);
		ans+=fir[i];
		ADD(a[i],1);
	}
	memset(BIT,0,sizeof(BIT));
	for(int i=n;i>=1;--i)
	{
		sec[i]=SUM(a[i]-1);
		ADD(a[i],1);
	}
	ll x;
	rep(i,1,q)
	{
		say(ans);
		read(x);
		ll pos=loc[x];
		ll res=fir[pos]+sec[pos];
		res-=find(0,pos-1,x+1,n)+find(pos,n,1,x-1);
		ans-=res;
		while(pos<=n)
		{
			modify(1,n,root[pos],x);
			pos+=lb(pos);
		}
	}
}