题目描述
对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。
题解
这道题的主要思路就是求出总的逆序对,然后再去掉某个数时删去这个数带来的贡献。
一个数若要对逆序对产生贡献,必须满足一下的两种情况:
- 如果这个数是较小的数,则贡献就是位置比它小,数值比它大,且消失时间比它晚点的个数。
- 如果这个数是较大的数,则贡献就是位置比它大,数值比它小,且消失时间比它晚点的个数。
这正好给了三个对应量的大小关系,是一个三维偏序问题。使用CDQ分治+树状数组即可求出每一份点的贡献。
代码如下(代码其实很短的):
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read(void)
{
int s = 0;char c = getchar();
while (c<'0' || c>'9') c = getchar();
while (c>='0' && c<='9') s = s*10+c-48,c = getchar();
return s;
}
struct node
{
int v,num,t,pos,ans;
friend bool operator < (node p1,node p2)
{
return p1.pos > p2.pos;
}
} ;
int n,m,ans;
int v[1000000];
node a[1000000];
node t[1000000];
struct TREE
{
int S[1000000];
#define lowbit(i) (i&-i)
void add(int x,int v)
{
for (int i=x;i<=n;i+=lowbit(i))
S[i] += v;
return;
}
int ask(int x)
{
int sum = 0;
for (int i=x;i>=1;i-=lowbit(i))
sum += S[i];
return sum;
}
} tree ,infc ;
void Readnum(void)
{
n = read(),
m = read();
for (int i=1;i<=n;++i)
{
a[i].v = read();
a[i].num = i;
a[i].t = m+1;
a[i].pos = i;
v[a[i].v] = i;
}
for (int i=1;i<=m;++i)
a[v[read()]].t = i;
return;
}
void Cdq1(int l,int r)
{
int mid = l+r >> 1;
if (l == r) return;
Cdq1(l,mid);
Cdq1(mid+1,r);
int h1 = l, h2 = mid+1,p = l-1;
while (h1 <= mid && h2 <= r)
{
if (a[h1].v > a[h2].v)
{
tree.add(a[h1].t,1);
t[++p] = a[h1++];
}
if (a[h1].v < a[h2].v)
{
a[h2].ans += tree.ask(n)-tree.ask(a[h2].t);
t[++p] = a[h2++];
}
}
while (h1 <= mid)
{
tree.add(a[h1].t,1);
t[++p] = a[h1++];
}
while (h2 <= r)
{
a[h2].ans += tree.ask(n)-tree.ask(a[h2].t);
t[++p] = a[h2++];
}
for (int i=l;i<=mid;++i) tree.add(a[i].t,-1);
for (int i=l;i<=r;++i) a[i] = t[i];
return;
}
void Get_ans(void)
{
ans = 0;
for (int i=n;i;--i)
{
ans += infc.ask(a[i].v);
infc.add(a[i].v,1);
}
return;
}
void Cdq2(int l,int r)
{
int mid = l+r >> 1;
if (l == r) return;
Cdq2(l,mid);
Cdq2(mid+1,r);
int h1 = l, h2 = mid+1,p = l-1;
while (h1 <= mid && h2 <= r)
{
if (a[h1].v < a[h2].v)
{
tree.add(a[h1].t,1);
t[++p] = a[h1++];
}
if (a[h1].v > a[h2].v)
{
a[h2].ans += tree.ask(n)-tree.ask(a[h2].t);
t[++p] = a[h2++];
}
}
while (h1 <= mid)
{
tree.add(a[h1].t,1);
t[++p] = a[h1++];
}
while (h2 <= r)
{
a[h2].ans += tree.ask(n)-tree.ask(a[h2].t);
t[++p] = a[h2++];
}
for (int i=l;i<=mid;++i) tree.add(a[i].t,-1);
for (int i=l;i<=r;++i) a[i] = t[i];
return;
}
bool cmp(node p1,node p2)
{
return p1.t<p2.t;
}
void output(void)
{
sort(a+1,a+n+1,cmp);
for (int i=1;i<=m;++i)
{
printf("%lld\n",ans);
ans -= a[i].ans;
}
return;
}
signed main(void)
{
freopen("inverse.in","r",stdin);
freopen("inverse.out","w",stdout);
Readnum();
Get_ans();
Cdq1(1,n);
sort(a+1,a+n+1);
Cdq2(1,n);
output();
return 0;
}