分篮子

题意:

一共有n(<=1e5)个元素,每一个元素都有三个关键字,a,b,c现在将这些元素分给A,B,C,这些元素价值在A手中的体现为,所有元素中最大的a值,在B中则是最大b值,C也是一样的,现在要有一种分法,求A+B+C的最小值。

题解:

首先想到的是n^2的算法,首先将所有元素按照a关键字从小到大,然后在开一个数组按照b关键字排序,然后从小到大枚举a,然后标记,然后从大到小枚举b,记录c的最大值,就可以一直更新答案。

既然知道了n^2的算法,现在想是否可以优化呢?枚举a是必然的,如果从小到大枚举a,无异是在b中删除元素,那么从大到小枚举a的话,就是在b中增加元素,删除是不好处理的,那么现在从大到小枚举a,那么现在的问题是在b中优化了。

b是从小到大排了序的,那么如果要选第i个,那么就需要知道(i + 1) ~ n中c的最大值,那么现在就会发现一个单调性便是在b中,随着b的增大,c的最大值一定是不上升的。

现在考虑新增加一个元素,那么这个元素会对它前面的比他的c小的b的c的最大值造成影响,那么现在就需要知道第一个小于等于这个元素的c值的位置,然后一并更新。

怎么知道那个位置呢?二分啊QAQ。怎么区间更新呢?线段树啊QAQ。

然后现在考虑求得答案,答案的组成是A+B+C,A是通过枚举的,现在的问题是求B+C的最小值,线段树也可以一并维护B+C了,线段树也要维护一个C的最大值。

代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 1e5 + 7;
int disc[N], dcnt, n, ans = 1e9 + 7;
int mi[N << 2], vc[N << 2], lz[N << 2];
struct node {int a, b, c;} ai[N];

#define mid (l + r >> 1)
#define lson o << 1, l, mid
#define rson o << 1 | 1, mid + 1, r

bool cmp (node a, node b) {return a.a < b.a;}

void pushdown (int o, int l, int r)
{
	if (!lz[o]) return;
	mi[o << 1] = disc[l] + lz[o], vc[o << 1] = lz[o], lz[o << 1] = lz[o];
	mi[o << 1 | 1] = disc[mid + 1] + lz[o], vc[o << 1 | 1] = lz[o], lz[o << 1 | 1] = lz[o];
	lz[o] = 0;
}

void update (int o, int l, int r, int L, int R, int x)
{
	if (l == L && r == R)
	{
		mi[o] = disc[L] + x;
		vc[o] = x;
		lz[o] = x;
		return;
	}
	pushdown(o, l, r);
	if (R <= mid) update (lson, L, R, x);
	else if (L > mid) update (rson, L, R, x);
	else update (lson, L, mid, x), update (rson, mid + 1, R, x);
	mi[o] = min (mi[o << 1], mi[o << 1 | 1]);
}

int query (int o, int l, int r, int x)
{
	if (l == r) return vc[o];
	pushdown(o, l, r);
	if (x <= mid) return query (lson, x);
	else if (x > mid) return query (rson, x);
}

int getpos (int l , int r, int d)
{
	while (l < r)
	{
		if (query(1, 1, dcnt, mid) < d) r = mid;
		else l = mid + 1;
	}
	return l;
}

void build (int o, int l, int r)
{
	if (l == r) 
	{
		mi[o] = disc[l];
		return;
	}
	build (lson);
	build (rson);
	mi[o] = min (mi[o << 1], mi[o << 1 | 1]);
}

int main ()
{
	scanf ("%d", &n);
	for (int i = 1; i <= n; ++ i)
	{
		scanf ("%d%d%d", &ai[i].a, &ai[i].b, &ai[i].c);
		disc[++dcnt] = ai[i].b;
	}
	disc[++dcnt] = 0;
	sort (ai + 1, ai + 1 + n, cmp);
	sort (disc + 1, disc + 1 + dcnt);
	dcnt = unique (disc + 1, disc + 1 + dcnt) - disc - 1;
	build (1, 1, dcnt);
	ans = ai[n].a;
	for (int i = n; i >= 1; -- i)
	{
		int p = lower_bound (disc + 1, disc + 1 + dcnt, ai[i].b) - disc;
		int pos = getpos(1, p, ai[i].c);
		if (pos <= p - 1) update (1, 1, dcnt, pos, p - 1, ai[i].c);
		ans = min (ans, ai[i - 1].a + mi[1]);
	}
	cout << ans << endl;
	return 0;
}

  

总结:

首先需要想到暴力,然后再优化就理所当然了,还有的就是要发现序列的单调性。

原文地址:https://www.cnblogs.com/xgtao/p/6034908.html