列表

详情


NC19883. [AHOI2008]逆序对

描述

小可可和小卡卡想到Y岛上旅游,但是他们不知道Y岛有多远。好在,他们找到一本古老的书,上面是这样说的: 下面是N个正整数,每个都在1~K之间。如果有两个数A和B,A在B左边且A大于B,我们就称这两个数为一个“逆序对”。你数一数下面的数字里有多少个逆序对,你就知道Y岛离这里的距离是多少千米了。 比如说,4 2 1 3 3里面包含了5个逆序对:(4, 2), (4, 1), (4, 3), (4, 3), (2, 1)。 可惜的是,由于年代久远,这些数字里有一部分已经模糊不清了,为了方便记录,小可可用“-1”表示它们。比如说,4 2 -1 -1 3 可能原来是4 2 1 3 3,也可能是4 2 4 4 3,也可能是别的样子。 小可可希望知道,根据他们看清楚的这部分数字,能不能推断出这些数字里最少能有多少个逆序对。

输入描述

第一行两个正整数N和K。
第二行N个整数,每个都是-1或是一个在1~K之间的数。

输出描述

一个正整数,即这些数字里最少的逆序对个数。

示例1

输入:

5 4
4 2 -1 -1 3

输出:

4

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++(clang++11) 解法, 执行用时: 8ms, 内存消耗: 3068K, 提交时间: 2021-02-14 11:05:07

#include<iostream>
#include<cstdio>

#include<cstring>
#define N 10010
#define M 110
using namespace std;
int f[N][M],a[N],p[N],n,m,ans,lc[N][M],rc[N][M],l,r,t;
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		if(a[i]!=-1)
		{
			for(int j=a[i]+1;j<=m;j++)
			ans+=p[j];
			p[a[i]]++; 
		}
		else
		r++;
	}
	memset(p,0,sizeof(p));
	for(int i=1;i<=n;i++)
	if(a[i]==-1)
	{
		l++;
		lc[l][0]=t;
		for(int j=1;j<=m;j++)
		lc[l][j]=lc[l][j-1]-p[j];
	}
	else
	p[a[i]]++,t++;
	memset(p,0,sizeof(p));
	t=0;
	for(int i=n;i>=1;i--)
	if(a[i]==-1)
	{
		rc[r][0]=0;
		for(int j=1;j<=m;j++)
		rc[r][j]=rc[r][j-1]+p[j-1];
		r--;
	}
	else
	p[a[i]]++;
	for(int i=1;i<=l;i++)
	{
		for(int j=1;j<=m;j++)
		f[i][j]=f[i-1][m]+lc[i][j]+rc[i][j];
		for(int j=2;j<=m;j++)
		f[i][j]=min(f[i][j],f[i][j-1]);
	}
	cout<<f[l][m]+ans; 
}

上一题