题意:
题解:
话说T2和T3的位置是故意的吧?还有std的代码真丑(就像隔壁苏珊婶婶做的苹果派一样)(基本上全机房的人都这么想)。
先说60%的数据,直接暴力枚举要取出的位置,暴力排序,然后每一次用树状数组或归并排序求逆序对数即可。
对于100%的数据,我们可以维护一个
f
[
i
]
f[i]
f[i]为
j
>
i
,
a
[
j
]
<
a
[
i
]
j>i,a[j]<a[i]
j>i,a[j]<a[i]的
j
j
j的个数,那么逆序对数就是
∑
f
[
i
]
\sum f[i]
∑f[i]。可以发现,经过一次操作后,被安排的位置的
f
[
i
]
f[i]
f[i]就变成了0,而其他位置不变。因为如果一个位置被安排了,那么它前面的数后面还是那些数,后面的比它小的位置也会被安排,而比它大的位置不变,而且被安排到它后面的仍然比它小。因为f[i]变成0之后就对答案没有贡献了,所以我们就不用再维护被安排过的位置了。
用线段树维护一下一个区间中f[i]的和与a[i]的最小值,操作时,如果区间的最小值
≤
a
j
k
\le a_{j_k}
≤ajk,就递归下去直到叶节点。如果某个位置被安排了,就把这个位置上的f[i]变为0,最小值变为INF。
代码:
#include<cstdio>
#include<algorithm>
#define maxn 200005
#define INF (LL)1e16
#define LL long long
using namespace std;
int n,m,a[maxn],tmp[maxn],bit[maxn],f[maxn],cnt;
inline int lowbit(int x) { return x&(-x); }
void Add(int x,int d) { for(;x<=n;x+=lowbit(x)) bit[x]+=d; }
int Query(int x)
{
int res=0;
for(;x;x-=lowbit(x)) res+=bit[x];
return res;
}
struct node { LL f,minx; } tree[maxn*4];
void Pushup(int i) { tree[i].f=tree[i<<1].f+tree[i<<1|1].f,tree[i].minx=min(tree[i<<1].minx,tree[i<<1|1].minx); }
void Build(int i,int l,int r)
{
if(l==r) { tree[i].f=f[l],tree[i].minx=a[l]; return; }
int mid=(l+r)>>1;
Build(i<<1,l,mid); Build(i<<1|1,mid+1,r);
Pushup(i);
}
void Modify(int i,int l,int r,int p,int d)
{
if(tree[i].minx>d) return;
if(l==r) { tree[i].minx=INF,tree[i].f=0; return; }
int mid=(l+r)>>1;
if(p<=mid) Modify(i<<1,l,mid,p,d);
Modify(i<<1|1,mid+1,r,p,d);
Pushup(i);
}
bool Query(int i,int l,int r,int p)
{
if(l==r) return tree[i].f!=0;
int mid=(l+r)>>1;
if(p<=mid) return Query(i<<1,l,mid,p);
else return Query(i<<1|1,mid+1,r,p);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=n;i;i--)
{
f[i]=Query(a[i]-1);
Add(a[i],1);
}
Build(1,1,n);
printf("%lld",tree[1].f);
while(m--)
{
int jk;
scanf("%d",&jk);
if(Query(1,1,n,jk)) Modify(1,1,n,jk,a[jk]);
printf(" %lld",tree[1].f);
}
}