Contest Hunter - OVOO

题意:

给出一颗有根树,每次可以从树上取包括根节点的一个连通块;

定义连通块的权值为块内边的权值之和;

询问第k小的连通块的权值是多少;

n<=100000,k<=100000;

此题为CH弱省胡策#1T3;


题解:

PoPoQQQ大爷好神!

这道题也是利用A*搜索来求K大值,但是状态比较难以表示;

先考虑怎么搜索,对于一个已经选完了的点集,下一次可能再选的点有哪些?

可能是上一次选的点的儿子,也可能是回溯到上一层,选比上一层选的点大的下一个点;

注意第二个可能性,这个是维护了一个有序性,保证方案权值递增并且不会出现重复方案;

这是一个A*的过程,我说的似乎不太好理解。。

每一种状态加入优先队列来跑A*;

贴个图来演示一下:


所以每次是将这些边集中最小的边拿出来,那就要用到一个堆;

而如果对于每个状态搞一个堆妥妥MLE;

那就用可持久化左偏树来搞(可持久化数据结构get√);

这样空间复杂度是O((n+k)logn)的(大概);

当然,将一个结点的所有儿子加入堆不能暴力,对每个结点维护一个儿子可并堆就好了;


代码:


#include<queue>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 110000
#define pr pair<ll,heap*>
using namespace std;
typedef long long ll;
struct heap
{
	heap *l, *r;
	int dis, no, val;
	heap();
}*null = new heap(), *root[N];
priority_queue<pr,vector<pr> ,greater<pr> >q;
heap::heap()
{
	l = null, r = null;
	dis = 0;
}
heap* Insert(heap *x, heap *y)
{
	if (x == null || y == null)
		return x == null ? y : x;
	if (x->val > y->val)
		swap(x, y);
	x->r = Insert(x->r, y);
	if (x->l->dis < x->r->dis)
		swap(x->l, x->r);
	x->dis = x->r->dis + 1;
	return x;
}
heap* merge(heap *x, heap *y)
{
	if (x == null || y == null)
		return x == null ? y : x;
	if (x->val > y->val)
		swap(x, y);
	heap *p = new heap();
	*p = *x;
	p->r = merge(p->r, y);
	if (p->l->dis < p->r->dis)
		swap(p->l, p->r);
	p->dis = p->r->dis + 1;
	return p;
}
int main()
{
	int n, m, i, j, k, x, y, v;
	ll ans;
	heap *p;
	null->l = null->r = null;
	null->no = null->val = 0;
	scanf("%d%d",&n,&k);
	for (i = 1; i <= n; i++)		root[i] = null;
	for (i = 1; i < n; i++)
	{
		scanf("%d%d", &x, &v);
		p = new heap();
		p->no = i+1, p->val = v;
		root[x]=Insert(root[x], p);
	}
	p = new heap();
	p->no = 1, p->val = 0;
	q.push(pr(0, p));
	for (i = 1, ans = 0; i <= k&&!q.empty(); i++)
	{
		pr now = q.top();
		q.pop();
		ans = now.first;
		p = now.second;
		x = p->no;
		v = p->val;
		p = merge(p->l, p->r);
		if(p!=null)
			q.push(pr(now.first - v + p->val, p));
		p = merge(p, root[x]);
		if(p!=null)
			q.push(pr(now.first + p->val, p));
	}
	printf("%d\n", ans % 998244353);
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值