正题
题目链接:https://www.luogu.com.cn/problem/P3157
题目大意
一个长度为nnn序列,每次删除一个数,求删除前的逆序对数量。
解题思路
时光倒流之后,我们变为每次加入一个数求逆序对数量。
我们将加入一个数的贡献分为后面和前面两部分
后面是加入时后方比他小的数的个数
前面是加入时前方比他大的数的个数
这里用aia_iai表示加入的时间先后,bib_ibi表示位置,cic_ici表示数值
若只考虑后方,我们可以发现加入xxx时iii有贡献当且仅当
[ai≤ax&bi>bx&ci<cx][a_i\leq a_x\ \&\ b_i>b_x\ \&\ c_i<c_x][ai≤ax & bi>bx & ci<cx]
然后可以发现这是一个三维偏序问题,我们用CDQ+CDQ+CDQ+树状数组维护。
做两次分别计算前后的贡献就可以了
时间复杂度O(nlog2n)O(n\log^2 n)O(nlog2n)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=1e5+10;
struct node{ll a,b,c;
}a[N];
ll n,m,ans[N],loc[N];
struct Tree_Array{#define lowbit(x) (x&-x)ll t[N];void Change(ll x,ll z){while(x<=n){t[x]+=z;x+=lowbit(x);}}ll Ask(ll x){ll ans=0;while(x){ans+=t[x];x-=lowbit(x);}return ans;}#undef lowbit(x)
}T;
bool cmp_a(node x,node y){if(x.a!=y.a) return x.a<y.a;return (x.b==y.b)?(x.c<y.c):(x.b<y.b);
}
bool cmp_zb(node x,node y)
{return (x.b==y.b)?(x.c<y.c):(x.b<y.b);}
bool cmp_fb(node x,node y)
{return (x.b==y.b)?(x.c>y.c):(x.b>y.b);}
void Cdq(ll l,ll r){if(l==r) return;ll mid=(l+r)>>1;Cdq(l,mid);Cdq(mid+1,r);sort(a+l,a+mid+1,cmp_zb);sort(a+mid+1,a+r+1,cmp_zb);ll k=l;for(ll i=mid+1;i<=r;i++){while(k<=mid&&a[k].b<=a[i].b)T.Change(a[k].c,1),k++;ans[a[i].a]+=T.Ask(a[i].c);}for(ll i=l;i<k;i++)T.Change(a[i].c,-1);return;
}
void Cdp(ll l,ll r){if(l==r) return;ll mid=(l+r)>>1;Cdp(l,mid);Cdp(mid+1,r);sort(a+l,a+mid+1,cmp_fb);sort(a+mid+1,a+r+1,cmp_fb);ll k=l;for(ll i=mid+1;i<=r;i++){while(k<=mid&&a[k].b>=a[i].b)T.Change(n-a[k].c+1,1),k++;ans[a[i].a]+=T.Ask(n-a[i].c+1);}for(ll i=l;i<k;i++)T.Change(n-a[i].c+1,-1);return;
}
int main()
{scanf("%lld%lld",&n,&m);for(ll i=1;i<=n;i++)scanf("%lld",&a[i].c),a[i].b=i,loc[a[i].c]=i,a[i].b=n-a[i].b+1;for(ll i=1,x;i<=m;i++)scanf("%lld",&x),a[loc[x]].a=m-i;sort(a+1,a+1+n,cmp_a);Cdq(1,n);sort(a+1,a+1+n,cmp_a);Cdp(1,n);for(ll i=1;i<m;i++)ans[i]+=ans[i-1];for(ll i=m-1;i>=0;i--)printf("%lld\n",ans[i]);
}