题目链接

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;
}

原文地址:http://www.cnblogs.com/zyyun/p/16784171.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性