[HOJ - 2430] Counting the algorithms (树状数组+贪心)

链接

http://acm.hit.edu.cn/problemset/2430

题意

给你一个由 1 − n   , n 1-n\ ,n 1n ,n个数组成的长度为 2 n 2n 2n的整数序列,其中 1 − n 1-n 1n每个数出现两次,现在定义一个数产生的一个 v a l u e value value为它出现的两个位置的差值,如 x x x出现在 a 、 b a、b ab两个位置,那么 v a l u e = b − a ( b > a ) value=b-a(b>a) value=ba(b>a),之后你需要从序列中删除掉这两个数,并且你需要把序列整合即需要填充空位,每次求值之后你都需要这样进行整合,现在需要你求所有数的最大 v a l u e value value和;

分析

  对于一个序列1、2、2、1,那么我们肯定不能先计算2,因为2计算之后会导致1的间距变小,所以我们需要先计算外面的,对于1、3、2、1、3、2,我们也肯定不能计算3,因为3计算后会导致1与2的结果都变小,虽然先计算1或者2也会变小,但是它只会减少1,多举几个例子发现需要贪心的从外往里进行计算(从右往左或从左往右都可以);
  考虑每个数产生的值可以转换为区间和,代表两个位置之间整数的个数,每删除一个数,就在它的两个位置插入一个-1;
  之后我们使用树状数组,树状数组的下标代表位置,开始的时候我们需要在每个位置都插入一个1,因为序列还没有进行删除操作;对于同一个数我们记录下它的两个位置,然后我们便开始从右往左或者从左往右遍历,遇到一个数我们就计算它产生的值即 s u m ( b ) − s u m ( a ) ( b > a ) sum(b)-sum(a)(b>a) sum(b)sum(a)(b>a),其中a、b即是该数出现的两个位置,之后我们在树状数组的这两个位置插入-1,表明该位置数已被删除,然后将该数字标记说明已经计算了,累加所有数的结果即可;

代码
#include <functional>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <set>
#include <map>

#define INF 0x7f7f7f7f
#define MAXN 100005
#define N 200005
#define P 2
#define MOD 99991

typedef long long ll;

namespace fastIO {
	//#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1<<22, stdin), p1 == p2) ? EOF : *p1++)
	//char buf[(1 << 22)], *p1 = buf, *p2 = buf;
	inline int read() {
		char c = getchar(); int x = 0, f = 1;
		while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); }
		while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
		return x * f;
	}
}

using namespace fastIO;
using namespace std;

int n, c[2 * MAXN], r[MAXN], a[2 * MAXN];

void insert(int x, int val) {
	for (int i = x; i <=2 * n; i += i & -i)
		c[i] += val;
}

int sum(int x) {
	if (x <= 0) return 0;
	int res = 0;
	for (int i = x; i > 0; i -= i & -i)
		res += c[i];
	return res;
}

int main() {
	while (cin >> n) {
		memset(c, 0, sizeof(c));
		for (int i = 1; i <= 2 * n; i++) {
			cin >> a[i];
			insert(i, 1);
			r[a[i]] = i;
		}
		int res = 0;
		for (int i = 1; i <= 2 * n; i++) {
			int t = r[a[i]];
			if (!t)continue;
			res += (sum(t) - sum(i));
			insert(t, -1); insert(i, -1);
			r[a[i]] = 0;
		}
		cout << res << endl;
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值