Contest Hunter 弱省胡策Round #1 OVOO

求第k小的含1号节点的联通块,可以理解为第k小的点集,对于一个点集,他有两种方式拓展,一个是将他的一个边缘节点替换成点集里其他节点延伸出去的最小的边的tail,一个是在点集里新加入一个边缘节点延伸出去的最小的边的tail。


于是我们维护一个点集的优先队列,对于当前的点集按以上两种方式拓展,塞进优先队列里面。

怎么才能知道一个点集边缘节点延伸出去的边的最小值呢?可以打一个可持久化的可并堆维护,每次操作把堆顶的边删掉,合并两个孩子,如果要新加一个节点,合并一下这个节点延伸出去的所有边,每次操作记得维护点集的权值。

这样做的话,优先队列就不需要维护点集,只需要维护当前的权值和以及当前延伸出去的边的堆就行了

有的点可能出度很大,所以每个节点延伸出去的所有边预处理一下,合并在一起。


(怎么感觉我讲的不清楚呢?有什么看不懂的可以留言,我看到都会回复的)



code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<ctime>
#include<cmath>
#include<string>
#include<vector>
#include<bitset>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;

void read( LL &x )
{
	char c; LL f=1;
	while( !( (c=getchar())>='0' && c<='9' ) )
		if( c == '-' ) f *= -1;
	x = c-'0';
	while( (c=getchar())>='0' && c<='9' )
		(x*=10) += c-'0';
	x *= f;
}

const LL maxn = 2010000;
const LL maxp = 1010000;
const LL Mod = 998244353;

queue< LL >q;
struct edge
{
	LL y,c,next;
}a[maxn]; 
LL len,first[maxp];
void ins( LL x,LL y,LL c )
{
	len++;
	a[len].y = y; a[len].c = c;
	a[len].next = first[x]; first[x] = len;
}

struct node
{
	LL lc,rc,c,y,dist;
}tr[maxp*5]; LL total;

LL n,k;

LL newnode( LL c,LL y )
{
	total++;
	tr[total].lc = tr[total].rc = 0;
	tr[total].c = c;
	tr[total].y = y;
	return total;
}
LL merge( LL x,LL y )
{
	if( !x || !y ) return x|y;
	if( tr[x].c > tr[y].c ) swap( x,y );
	LL ret = ++total;
	tr[ret] = tr[x];
	LL k = merge( tr[ret].rc,y );
	if( tr[tr[ret].lc].dist <= tr[k].dist ) swap( tr[ret].lc,k );
	tr[ret].rc = k; 
	if( k ) tr[ret].dist = tr[k].dist+1;
	else tr[ret].dist = 0;
	return ret;
}

struct G
{
	LL x,d;
};
bool operator <( G x,G y )
{
	return x.d > y.d;
}
priority_queue< G >Q;
LL rootp[maxn];

int main()
{
	len = 0; memset( first,0,sizeof first );
	LL x,y,c;
	
	read( n ); read( k );
	for( LL i=2;i<=n;i++ )
	{
		read( x ); read( y );
		ins( x,i,y );
	}
	
	q.push( 1 );
	while( !q.empty() )
	{
		x = q.front(); q.pop();
		for( LL k=first[x];k;k=a[k].next )
		{
			y = a[k].y;
			rootp[x] = merge( rootp[x],newnode( a[k].c,y ) );
			q.push( y );
		}
	}
	//rootp[0] = newnode( 0,1 );
	
	LL ans=0;
	G t0;
	t0.x = rootp[1]; t0.d = tr[rootp[1]].c;
	Q.push( t0 );
	k--;
	while( k-- )
	{
		if( Q.empty() ) break;
		G now = Q.top(); Q.pop();
		ans = now.d;
		
		LL mc = merge( tr[now.x].lc,tr[now.x].rc );
		if( mc )
		{
			G t1;
			t1.d = now.d + tr[mc].c - tr[now.x].c;
			t1.x = mc;
			Q.push( t1 );
		}
		G t2;
		t2.x = merge( mc,rootp[tr[now.x].y] );
		t2.d = now.d + tr[t2.x].c;
		if( t2.x ) Q.push( t2 );
	}
	printf("%I64d\n",ans%Mod);
	
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值