2021-09-08

本文介绍了作者初次尝试使用Vim编辑器编写代码的经历,并详细解析了一个利用O(nlogn)复杂度解决区间不交叉问题的算法。通过排序、建树和动态规划优化,实现了对最大点亮边数的求解。代码中涉及了数据结构、排序、优先队列和递归等概念。
摘要由CSDN通过智能技术生成

2020.9.2 校训

第一次尝试主用vim写代码,感觉还不错

J

首先看到这题的第一眼我的反应是先来一个一个 O ( n log ⁡ n ) O(n \log{n}) O(nlogn)的排序(数据范围给的也很符合)。排序之后,可以根据“区间不交叉”这一性质建树,每个节点的父节点完全包含其所有儿子(需要手动添加一个在[-1,1e6]跨度上权值为0的节点作为根节点)。
粗看可以用O ( n 2 ) (n^2) (n2)的DP来做,大概就是dp[i][j]表示“第i大的节点为根的子树上,最多允许点亮j条边”子问题的解。然而根据题解中提到的闵可夫斯基定理,可以利用f[i][j]-f[i][j-1]不增的性质进行优化,最终复杂度为 O ( n log ⁡ n ) O(n \log n) O(nlogn)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int n, s, h[250001];

struct edge
{
	int id;
	int s, e, fa;
	ll v;
	vector<int> des;
	void init(int idx, int S, int E, int V)
	{
		id = idx;
		s = S;
		e = E;
		v = V;
		fa = -1;
	}
} a[250001];

priority_queue<ll> q[250001];

bool cmp(const edge &x, const edge &y)
{
	if (x.s == y.s)
		return (x.e > y.e);
	return x.s < y.s;
}

vector<ll> t;

void visit(int idx)
{
	int maxi = 0, maxs = 0;
	for (auto j : a[idx].des)
	{
		visit(j);
		if (q[h[j]].size() > maxs)
		{
			maxs = q[h[j]].size();
			maxi = j;
		}
	}
	h[idx] = h[maxi];
	for (auto j : a[idx].des)
	{
		if (j == maxi)
			continue;
		for (t.clear(); !q[h[j]].empty();)
		{
			t.push_back(q[h[idx]].top() + q[h[j]].top());
			q[h[idx]].pop();
			q[h[j]].pop();
		}
		for (auto tmp : t)
			q[h[idx]].push(tmp);
	}
	if (!h[idx])
		h[idx] = ++s;
	if (idx)
		q[h[idx]].push(a[idx].v);
}

int main()
{
	// freopen("jin", "r", stdin);
	scanf("%d", &n);
	a[0].init(0, -1, 1e6 + 2, 0);
	for (int i = 1, s, e, v; i <= n; ++i)
	{
		scanf("%d%d%d", &s, &e, &v);
		a[i].init(i, s, e - 1, v);
	}
	sort(a, a + n + 1, cmp);
	int fa = 0;
	for (int i = 1; i <= n; ++i)
	{
		a[i].id = i;
		while (a[i].s > a[fa].e && fa != 0)
		{
			fa = a[fa].fa;
		}
		a[fa].des.push_back(i);
		a[i].fa = fa;
		fa = i;
	}
	visit(0);
	ll ans = 0;
	for (int i = 1; i <= n; ++i)
	{
		if (!q[h[0]].empty())
		{
			ans += q[h[0]].top();
			q[h[0]].pop();
		}
		printf("%lld ", ans);
	}
	puts("");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值