【bzoj2809】dispatching 主席树+dfs序

1 篇文章 0 订阅

AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=2809

【题解】

对于题上给的树,我们在它的dfs序上瞎搞。

我们按照dfs序建主席树,主席树中以花费的离散值为权,并维护总花费。

然后查询答案的时候,枚举每个点为管理者的情况,计算答案。

这样问题就转化为了在子树内找到最多的点,使得这些点的花费小于m,用主席树就能完美解决这个问题。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
#define FILE "read"
#define MAXN 100010
#define up(i,j,n) for(ll i=j;i<=n;++i)
#define dn(i,j,n) for(ll i=j;i>=n;--i)
#define cmax(a,b) a=max(a,b)
#define cmin(a,b) a=min(a,b)
namespace INIT{
	char buf[1<<15],*fs,*ft;
	inline char getc(){return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;}
	inline ll read(){
		ll x=0,f=1;  char ch=getc();
		while(!isdigit(ch))  {if(ch=='-')  f=-1;  ch=getc();}
		while(isdigit(ch))  {x=x*10+ch-'0';  ch=getc();}
		return x*f;
	}
}using namespace INIT;
struct node{ll y,next;}e[MAXN*2];
ll n,m,len,dfs_clock,tot,cnt,ans,Link[MAXN],a[MAXN],b[MAXN],num[MAXN],hash[MAXN],root[MAXN],dfn[MAXN],size[MAXN];
ll sum[MAXN<<5],cost[MAXN<<5],son[MAXN<<5][2];
void insert(ll x,ll y){e[++len].next=Link[x];Link[x]=len;e[len].y=y;}
void dfs(ll x,ll fa){
	dfn[++dfs_clock]=x;  size[x]=1;
	for(ll i=Link[x];i;i=e[i].next)
		if(e[i].y!=fa)  dfs(e[i].y,x),size[x]+=size[e[i].y];
}
ll find(ll x){
	ll l=1,r=tot;
	while(l+1<r){
		ll mid=(l+r)>>1;
		if(hash[mid]<=x)  l=mid;
		else r=mid;
	}
	return hash[l]==x?l:r;
}
ll newroot(ll last,ll v){
	sum[++cnt]=sum[last]+1; cost[cnt]=cost[last]+v;
	son[cnt][0]=son[last][0];  son[cnt][1]=son[last][1];
	return cnt;
}
void updata(ll l,ll r,ll &root,ll last,ll x){
	root=newroot(last,hash[x]);
	if(l==r)  return;
	ll mid=(l+r)>>1;
	if(x<=mid)  updata(l,mid,son[root][0],son[last][0],x);
	else updata(mid+1,r,son[root][1],son[last][1],x);
}
ll ask(ll l,ll r,ll root,ll last,ll lim){
	if(l==r) return min(lim/hash[l],sum[root]-sum[last]);
	ll mid=(l+r)>>1,temp=cost[son[root][0]]-cost[son[last][0]];
	if(temp>lim)  return ask(l,mid,son[root][0],son[last][0],lim);
	else return sum[son[root][0]]-sum[son[last][0]]+ask(mid+1,r,son[root][1],son[last][1],lim-temp);
}
int main(){
	freopen(FILE".in","r",stdin);
	freopen(FILE".out","w",stdout);
	n=read();  m=read();
	up(i,1,n){
		ll x=read(); a[i]=num[i]=read(); b[i]=read();
		if(x) insert(i,x),insert(x,i);
	}
	dfs(1,0);  sort(num+1,num+n+1);  hash[++tot]=num[1];
	up(i,2,n)  if(num[i]!=num[i-1])  hash[++tot]=num[i];
	up(i,1,n)  updata(1,tot,root[i],root[i-1],find(a[dfn[i]]));
	up(i,1,n)  cmax(ans,b[dfn[i]]*ask(1,tot,root[i+size[dfn[i]]-1],root[i-1],m));
	printf("%lld\n",ans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值