[BZOJ1786][BZOJ1831]逆序对

试题描述

输入

输出

输入示例

5 4
4 2 -1 -1 3

输出示例

4

数据规模及约定

见“输入

题解

首先这题有一个性质,即,填的数从左到右一定不降。证明不妨读者自己yy(提示:用先按降序填,交换后答案一定不会更差的思想证)。

那么新添的数字一定不会造出逆序对了。

然后设计 dp,设 f(i, j) 表示考虑前 i 个打“-1”的位置,最后一个(第 i 个)位置填写数字 j 时,与已经固定的数字产生最少的逆序对数。

有 f(i, j) = min{ f(i-1, x) | 0 < x ≤ j } + lager(pos[i], j) + smaller(pos[i], j),其中,pos[i] 表示第 i 个“-1”再原数列中的位置,larger(i, j) 表示第 1~i-1 个已知数中比 j 大的数的个数,smaller(i, j) 表示第 i+1~n 个已知数中比 j 小的数的个数。

答案是 min{ f(cnt, x) | 0 < x ≤ k } + ans,其中 cnt 表示 -1 的个数,ans 表示已知数字中逆序对个数。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <stack>
#include <vector>
#include <queue>
#include <cstdlib>
using namespace std;

const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *tail;
inline char Getchar() {
    if(Head == tail) {
        int l = fread(buffer, 1, BufferSize, stdin);
        tail = (Head = buffer) + l;
    }
    return *Head++;
}
int read() {
	int x = 0, f = 1; char c = Getchar();
	while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = Getchar(); }
	while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = Getchar(); }
	return x * f;
}

#define maxn 10010
#define maxk 110
#define oo 2147483647
int n, k, A[maxn], f[maxn][maxk], la[maxn][maxk], sm[maxn][maxk], pos[maxn], cnt;

int c[maxn];
void add(int x) { for(; x <= k; x += x & -x) c[x]++; return ; }
int sum(int x) { int res = 0; for(; x; x -= x & -x) res += c[x]; return res; }

int main() {
	n = read(); k = read();
	for(int i = 1; i <= n; i++) A[i] = read();

	int ans = 0;
	for(int i = n; i; i--) {
		if(A[i] >= 0) add(A[i]), ans += sum(A[i] - 1);
		for(int j = 2; j <= k; j++) sm[i][j] = sum(j - 1);
	}
	memset(c, 0, sizeof(c));
	for(int i = 1; i <= n; i++) {
		if(A[i] >= 0) add(k - A[i] + 1);
		else pos[++cnt] = i;
		for(int j = 1; j < k; j++) la[i][j] = sum(k - j);
	}
	for(int i = 1; i <= k; i++) f[0][i] = 0;
	for(int i = 1; i <= cnt; i++)
		for(int j = 1; j <= k; j++) {
			f[i][j] = oo;
			for(int x = 1; x <= j; x++) f[i][j] = min(f[i][j], f[i-1][x]);
			f[i][j] += la[pos[i]][j] + sm[pos[i]][j];
		}

	int sum = oo;
	for(int i = 1; i <= k; i++) sum = min(sum, f[cnt][i]);
	printf("%d\n", ans + sum);

	return 0;
}
时间: 2024-10-14 15:56:57

[BZOJ1786][BZOJ1831]逆序对的相关文章

BZOJ1786 [Ahoi2008]Pair 配对 动态规划 逆序对

欢迎访问~原文出处--博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1786 题意概括 给出长度为n的数列,只会出现1~k这些正整数.现在有些数写成了-1,这些-1可以变成任何数. 求把这些-1变成1~k中的正整数之后,最少的逆序对个数为多少. 题解 我们可以判断,这些-1中写的数字一定是单调不降的. 为什么?我们把答案序列的所有-1位抽出来,如果答案序列中有一组是逆序的,那么交换他们,一定可以保证小的那个换到大的那个的位置的时候,它左右产生的逆序对数一定比大的原先

BZOJ1831: [AHOI2008]逆序对

1831: [AHOI2008]逆序对 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 341  Solved: 226[Submit][Status] Description 小可可和小卡卡想到Y岛上旅游,但是他们不知道Y岛有多远.好在,他们找到一本古老的书,上面是这样说的: 下面是N个正整数,每个都在1~K之间.如果有两个数A和B,A在B左边且A大于B,我们就称这两个数为一个“逆序对”.你数一数下面的数字里有多少个逆序对,你就知道Y岛离这里的距离

BZOJ1786: [Ahoi2008]Pair 配对/1831: [AHOI2008]逆序对

这两道题是一样的. 可以发现,-1变成的数是单调不降. 记录下原有的逆序对个数. 预处理出每个点取每个值所产生的逆序对个数,然后dp转移. #include<cstring> #include<iostream> #include<cstdio> #include<map> #include<cmath> #include<algorithm> #define rep(i,l,r) for (int i=l;i<=r;i++)

bzoj1831【AHOI2008】逆序对

1831: [AHOI2008]逆序对 Time Limit: 10 Sec  Memory Limit: 64 MB Submit: 485  Solved: 341 [Submit][Status][Discuss] Description 小可可和小卡卡想到Y岛上旅游,但是他们不知道Y岛有多远.好在,他们找到一本古老的书,上面是这样说的: 下面是N个正整数,每个都在1~K之间.如果有两个数A和B,A在B左边且A大于B,我们就称这两个数为一个"逆序对".你数一数下面的数字里有多少个

动态规划 BZOJ1831 [AHOI2008]逆序对

1831: [AHOI2008]逆序对 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 660  Solved: 469[Submit][Status][Discuss] Description 小可可和小卡卡想到Y岛上旅游,但是他们不知道Y岛有多远.好在,他们找到一本古老的书,上面是这样说的: 下面是N个正整数,每个都在1~K之间.如果有两个数A和B,A在B左边且A大于B,我们就称这两个数为一个"逆序对".你数一数下面的数字里有多少个逆序

bzoj1786: [Ahoi2008]Pair 配对&amp;&amp;1831: [AHOI2008]逆序对

一个自以为很对的东西,我们往-1放的数肯定是不增的. 然后就预处理一下,假如i这个位置放j会多多少逆序对. DP一下,我的复杂度应该是O(n*m^2)的,然而你随便搞都能省掉一个m吧,我算了算好像可以过就不管了. 注意树状数组的时候getsum是a[i]-1,相同是不算逆序对的 #include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorit

codeforces 414C C. Mashmokh and Reverse Operation(归并排序求逆序对)

题目链接: C. Mashmokh and Reverse Operation time limit per test 4 seconds memory limit per test 512 megabytes input standard input output standard output Mashmokh's boss, Bimokh, didn't like Mashmokh. So he fired him. Mashmokh decided to go to university

求逆序对(线段树版)

一个序列a1,a2,a3...aN,求出满足:ai > aj 且 i < j 的个数. 一个最容易想到的方法就是枚举所有的i,j看看是否满足,显然是O(n^2)的复杂度.不够好. 可以这样考虑,开一个数组保存这n个数出现的位置和对应的次数,这个数组要开到a数组里最大的那个数MAX,也就是hash,初始状态数组里没有元素,每个数对应的个数都是0. 如果考虑第i个数,找到比它大的所有的数 的个数,查找的范围即 ai+1~MAX,这就是到i这个位置的逆序对的总和,接着把a[i]这个数添加到数组里,也

剑指offer: 数组中的逆序对

1. 最简单的思路,对每个值,遍历与其逆序的数组对:但时间复杂度太高: 2. 归并排序的思路: 先将数组分隔成子数组,先统计出子数组内的逆序对的数目,然后统计两个相邻子数组之间的逆序对的数目: int InversePairsCore(int *data, int * copy, int start, int end) { //递归介绍条件,只剩一个元素 if(start==end) { copy[start]=data[start]; return 0; } int length=(end-s