wyx528命题
题解:树状数组+线段树
首先利用权值树状数组求出以i个点开头的逆序对数。
选出一个位置i,将i-n中所有小于等于p[i]的位置选出来重新排好序后放回,可以发现这样做对于位于i前面的点的逆序对数没有影响,因为原先在后面现在还在后面,对于i-n中大于p[i]的点的逆序对数也没有影响因为无论怎么移动,选出的位置一定小于他。那么有影响的其实就是选出了的点,他们的逆序对数变为了0。
用线段树维护区间最小值所在的位置(如果两个位置的值一样,那么优先选择后面的位置),每次将一个位置的逆序对数清零后,将这个位置的数值付成极大值,这样就可以保证每个点至多修改一次,然后从i-n的区间中一直修改,知道a[i]=inf位置,输出此时序列中的总逆序对数。时间复杂度应该在o(nlogn)左右。
刚开始还想到了树套数的写法,用区间线段树套权值线段树,貌似也可以,但是时间复杂度不如这种方法优越。
#include
#include
#include
#include
#include
#define N 500003
#define LL long long
#define inf 1000000000
using namespace std;
int n,m;
int a[N],b[N],p[N],cnt;
LL f[N],tr[N*4],ans;
int tree[N*4];
int cmp(int x,int y)
{
return b[x]=ll&&r<=rr)
return tree[now];
int mid=(l+r)/2;
int ans=0;
if (ll<=mid)
ans=query(now<<1,l,mid,ll,rr);
if (rr>mid)
{
int t=query(now<<1|1,mid+1,r,ll,rr);
if (ans==0) ans=t;
else ans=pd(ans,t);
}
return ans;
}
void pointchange(int now,int l,int r,int x)
{
if (l==r)
{
a[l]=inf;
f[l]=0;
return ;
}
int mid=(l+r)/2;
if (x<=mid)
pointchange(now<<1,l,mid,x);
else
pointchange(now<<1|1,mid+1,r,x);
update(now);
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%d",&b[i]),p[i]=i;
sort(p+1,p+n+1,cmp);
cnt=0;
for (int i=1;i<=n;i++)
if (b[p[i]]!=b[p[i-1]]) a[p[i]]=++cnt;
else a[p[i]]=cnt;
for (int i=n;i>=1;i--)
{
change(a[i],1);
f[i]=sum(a[i]-1);
ans+=f[i];
}
printf("%lld\n",ans);
build(1,1,n);
for (int i=1;i<=m;i++)
{
int x; scanf("%d",&x);
while (a[x]!=inf)
{
int t=query(1,1,n,x,n);
ans-=f[t];
pointchange(1,1,n,t);
}
printf("%lld\n",ans);
}
}