2819. 动态逆序对
题目链接
2819. 动态逆序对
对于序列 \(A\),它的逆序对数定义为满足 \(i < j\),且 \(A_i > A_j\) 的数对 \((i,j)\) 的个数。
给 \(1\) 到 \(n\) 的一个排列,按照某种顺序依次删除 \(m\) 个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。
输入格式
第一行包含两个整数 \(n\) 和 \(m\),即初始元素的个数和删除的元素个数。
以下 \(n\) 行每行包含一个 \(1\) 到 \(n\) 之间的正整数,即初始排列。
以下 \(m\) 行每行一个正整数,依次为每次删除的元素。
输出格式
输出包含 \(m\) 行,依次为删除每个元素之前,逆序对的个数。
数据范围
\(1 \le n \le 10^5\),
\(1 \le m \le 50000\)
输入样例:
5 4
1
5
3
4
2
5
1
4
2
输出样例:
5
2
2
1
样例解释
给定序列变化依次为 \((1,5,3,4,2) \to (1,3,4,2) \to (3,4,2) \to (3,2) \to (3)\)。
解题思路
cdq分治
按时间增加一个维度:时间戳,每次删除都有一个唯一的时间戳 \(t\),其他没有删除的数的时间戳可任意指定,为方便后面树状数组的处理,每次删除将时间戳倒序处理,对于元素 \(i\),求出删除 \(i\) 之前,即 \(t_i\) 之前,满足 \(i<j,a[i]>a[j]\) 或 \(i>j,a[i]<a[j]\) 的对数,即对于 \(t_i<t_j\),求出 \(i<j,a[i]>a[j]\) 或 \(i>j,a[i]<a[j]\) 的对数即可,故需要两遍 \(cdq分治\),另外,里面双指针的位置等信息需要考虑一下
- 时间复杂度:\(O(nlgn^2n)\)
代码
// Problem: 动态逆序对
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/2821/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=1e5+5;
int n,m,pos[N],tr[N];
LL res[N];
struct A
{
int p,t,res;
}a[N],b[N];
void add(int x,int y)
{
for(;x<N;x+=x&-x)tr[x]+=y;
}
int ask(int x)
{
int res=0;
for(;x;x-=x&-x)res+=tr[x];
return res;
}
void merge_sort(int l,int r)
{
if(l>=r)return ;
int mid=l+r>>1;
merge_sort(l,mid),merge_sort(mid+1,r);
int j=l,i=mid+1;
while(j<=mid&&i<=r)
if(a[i].p<a[j].p)add(a[i].t,1),i++;
else
a[j].res+=ask(a[j].t-1),j++;
while(j<=mid)a[j].res+=ask(a[j].t-1),j++;
for(int k=mid+1;k<i;k++)add(a[k].t,-1);
i=mid,j=r;
while(i>=l&&j>=mid+1)
if(a[i].p>a[j].p)add(a[i].t,1),i--;
else
a[j].res+=ask(a[j].t-1),j--;
while(j>=mid+1)a[j].res+=ask(a[j].t-1),j--;
for(int k=i+1;k<=mid;k++)add(a[k].t,-1);
int k=0;
i=l,j=mid+1;
while(i<=mid&&j<=r)
if(a[i].p<a[j].p)b[++k]=a[i++];
else
b[++k]=a[j++];
while(i<=mid)b[++k]=a[i++];
while(j<=r)b[++k]=a[j++];
for(int i=l,j=1;j<=k;j++,i++)a[i]=b[j];
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i].p,pos[a[i].p]=i;
for(int i=n,j=1;j<=m;j++,i--)
{
int p;
cin>>p;
a[pos[p]].t=i;
pos[p]=-1;
}
for(int i=1,j=n+1-m;i<=n;i++)
if(pos[a[i].p]!=-1)a[i].t=--j;
merge_sort(1,n);
for(int i=1;i<=n;i++)res[a[i].t]=a[i].res;
for(int i=1;i<=n;i++)res[i]+=res[i-1];
for(int i=n;i>=n+1-m;i--)cout<<res[i]<<'\n';
return 0;
}