BZOJ2809 [Apio2012]dispatching 解题报告

题目是中文的就不给题意了~





题解:  忍者组织可以看成一棵树,根是master,题意其实就是要在找到的点的权值和不超过给定的M的情况下,在一棵子树上找尽量多的点使得点的数量×子树的根的L最大,求这个最大值。

DFS,对于一个结点,先在他孩子的子树找点的最大值,再将他的孩子找到的点合并,如果权值和超过了M,就删掉最大的点,不断操作直到和小于M,计算这棵子树的最大值。对于合并和查找操作用左偏树(可并堆)维护


code:

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

const LL maxn = 210000;
struct edge
{
	LL y,next;
}a[maxn<<1]; LL len,first[maxn];
void insert( LL x,LL y )
{
	len++;
	a[len].y = y;
	a[len].next = first[x]; first[x] = len;
}

struct tree
{
	LL lc,rc,c,dist;
}tr[maxn]; LL n,m;
LL fa[maxn],s[maxn],c[maxn],siz[maxn];
LL ans = 0;

LL find_( LL x )
{
	if( fa[x] == x ) return x;
	return fa[x] = find_( fa[x] );
}
LL merge( LL x,LL y )
{
	if( !x ) return y;
	if( !y ) return x;
	if( tr[x].c < tr[y].c )  swap( x,y );
	LL k = merge( tr[x].rc,y );
	tr[x].rc = k; fa[k] = x;
	if( tr[tr[x].lc].dist < tr[tr[x].rc].dist ) swap( tr[x].lc,tr[x].rc );
	tr[x].dist = tr[tr[x].rc].dist+1;
	return x;
}
void dfs( LL x,LL f )
{
	siz[x] = 1;
	c[x] = tr[x].c;
	for( LL k=first[x];k;k=a[k].next )
	{
		LL y = a[k].y;
		if( y != f )
		{
			dfs( y,x );
			merge( find_( x ),find_( y ) );
			c[x] += c[y];
			siz[x] += siz[y];
		}
	}
	while( c[x] > m )
	{
		LL f1 = find_( x );
		c[x] -= tr[f1].c;
		siz[x] --;
		LL k = merge( tr[f1].lc,tr[f1].rc );
		fa[f1] = k;
		fa[k] = k;
	}
	ans = max( ans,siz[x]*s[x] );
}

int main()
{
	LL x,t;
	memset( first,0,sizeof first ); len = 0;
		scanf("%lld%lld",&n,&m);
		for( LL i=1;i<=n;i++ )
		{
			fa[i] = i;
			tr[i].dist = tr[i].lc = tr[i].rc = 0;
			scanf("%lld%lld%lld",&x,&tr[i].c,&s[i]);
			if( i != 1 )
			{
				insert( i,x );
				insert( x,i );
			}
		}
		dfs( 1,0 );
		printf("%lld\n",ans);
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值