BZOJ 2809 APIO2012 dispatching Treap+启发式合并 / 可并堆

题目大意:给定一棵树,选定一棵子树中的一些点,薪水和不能超过m,求点的数量*子树根节点的领导能力的最大值

考虑对于每个节点,我们维护一种数据结构,在其中贪心寻找薪金小的雇佣。

每个节点暴力重建一定不行,我们考虑可并数据结构,每个节点将子节点的信息直接合并即可

可以用启发式合并的Treap,也可以用可并堆

今天特意去学了这玩应0.0 先写了左偏树 然后又写了下随机堆…… 后者速度上更快一些 不过建议从左偏树开始学起

总之平衡树常数各种大就是了0.0

Treap+启发式合并

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 100100
using namespace std;
typedef long long ll;
struct abcd{
	abcd *ls,*rs;
	int key;
	int cnt,siz;
	ll num,sum;
	abcd (ll x,int y);
	void Maintain();
}*null=new abcd(0,0),*tree[M];
struct edge{
	int to,next;
}table[M];
int head[M],tot;
int n,root;
ll m,ans,leadership[M];
void Add(int x,int y)
{
	table[++tot].to=y;
	table[tot].next=head[x];
	head[x]=tot;
}
abcd :: abcd(ll x,int y)
{
	ls=rs=null;
	sum=x*y;
	num=x;
	cnt=siz=y;
	key=y?rand():0;
}
void abcd :: Maintain()
{
	siz=ls->siz+rs->siz+cnt;
	sum=ls->sum+rs->sum+num*cnt;
}
void Zig(abcd *&x)
{
	abcd *y=x->ls;
	x->ls=y->rs;
	y->rs=x;
	x=y;
	x->rs->Maintain();
}
void Zag(abcd *&x)
{
	abcd *y=x->rs;
	x->rs=y->ls;
	y->ls=x;
	x=y;
	x->ls->Maintain();
}
void Insert(abcd *&x,ll y,int z)
{
	if(x==null)
	{
		x=new abcd(y,z);
		return ;
	}
	if(y==x->num)
		x->cnt+=z;
	else if(y<x->num)
	{
		Insert(x->ls,y,z);
		if(x->ls->key>x->key)
			Zig(x);
	}
	else
	{
		Insert(x->rs,y,z);
		if(x->rs->key>x->key)
			Zag(x);
	}
	x->Maintain();
}
int Query(abcd *x,ll y)
{
	if(x==null)
		return 0;
	ll temp=x->ls->sum;int re=0;
	if(y<=temp)	return Query(x->ls,y);
	re+=x->ls->siz;y-=temp;
	if(y<=x->num*x->cnt)
		return re+y/x->num;
	re+=x->cnt;y-=x->num*x->cnt;
	return re+Query(x->rs,y);
}
void Decomposition(abcd *&x,int y)
{
	if(x==null)
		return ;
	Decomposition(x->ls,y);
	Decomposition(x->rs,y);
	Insert(tree[y],x->num,x->cnt);
	delete x;
	x=null;
}
void Tree_DP(int x)
{
	int i;
	for(i=head[x];i;i=table[i].next)
	{
		Tree_DP(table[i].to);
		if(tree[x]->siz<tree[table[i].to]->siz)
			swap(tree[x],tree[table[i].to]);
		Decomposition(tree[table[i].to],x);
	}
	ans=max(ans,leadership[x]*Query(tree[x],m));
}
int main()
{

	//freopen("2809.in","r",stdin);
	//freopen("2809.out","w",stdout);

	int i,fa;
	ll x;
	cin>>n>>m;
	for(i=1;i<=n;i++)
	{
		scanf("%d%lld%lld",&fa,&x,&leadership[i]);
		if(!fa) root=i;
		else Add(fa,i);
		tree[i]=new abcd(x,1);
	}
	Tree_DP(root);
	cout<<ans<<endl;
}
//lld!!

左偏树

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 100100
using namespace std;
struct abcd{
	abcd *ls,*rs;
	int num,h;
	abcd(int x);
}*null=new abcd(0),*tree[M];
struct edge{
	int to,next;
}table[M];
int head[M],tot;
int n,m,root,leadership[M],sum[M],size[M];
long long ans;
void Add(int x,int y)
{
	table[++tot].to=y;
	table[tot].next=head[x];
	head[x]=tot;
}
abcd :: abcd(int x)
{
	ls=rs=null;
	num=x;
	if(x) h=0;
	else h=-1;
}
abcd* Merge(abcd *x,abcd *y)
{
	if(x==null) return y;
	if(y==null) return x;
	if(x->num<y->num)
		swap(x,y);
	x->rs=Merge(x->rs,y);
	if(x->ls->h<x->rs->h)
		swap(x->ls,x->rs);
	x->h=x->rs->h+1;
	return x;
}
void Tree_DP(int x)
{
	int i;
	for(i=head[x];i;i=table[i].next)
	{
		Tree_DP(table[i].to);
		tree[x]=Merge(tree[x],tree[table[i].to]);
		sum[x]+=sum[table[i].to];
		size[x]+=size[table[i].to];
		while(sum[x]>m)
		{
			sum[x]-=tree[x]->num;
			--size[x];
			tree[x]=Merge(tree[x]->ls,tree[x]->rs);
		}
	}
	ans=max(ans,(long long)size[x]*leadership[x]);
}
int main()
{
	int i,fa,x;
	cin>>n>>m;
	for(i=1;i<=n;i++)
	{
		scanf("%d%d%d",&fa,&x,&leadership[i]);
		if(!fa) root=i;
		else Add(fa,i);
		tree[i]=new abcd(x);
		sum[i]=x;
		size[i]=1;
	}
	Tree_DP(root);
	cout<<ans<<endl;
}

随机堆

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 100100
using namespace std;
struct abcd{
	abcd *ls,*rs;
	int num;
	abcd(int x);
}*null=new abcd(0),*tree[M];
struct edge{
	int to,next;
}table[M];
bool son;
int head[M],tot;
int n,m,root,leadership[M],sum[M],size[M];
long long ans;
void Add(int x,int y)
{
	table[++tot].to=y;
	table[tot].next=head[x];
	head[x]=tot;
}
abcd :: abcd(int x)
{
	ls=rs=null;
	num=x;
}
abcd* Merge(abcd *x,abcd *y)
{
	if(x==null) return y;
	if(y==null) return x;
	if(x->num<y->num)
		swap(x,y);
	if(son^=1)
		x->rs=Merge(x->rs,y);
	else
		x->ls=Merge(x->ls,y);
	return x;
}
void Tree_DP(int x)
{
	int i;
	for(i=head[x];i;i=table[i].next)
	{
		Tree_DP(table[i].to);
		tree[x]=Merge(tree[x],tree[table[i].to]);
		sum[x]+=sum[table[i].to];
		size[x]+=size[table[i].to];
		while(sum[x]>m)
		{
			sum[x]-=tree[x]->num;
			--size[x];
			tree[x]=Merge(tree[x]->ls,tree[x]->rs);
		}
	}
	ans=max(ans,(long long)size[x]*leadership[x]);
}
int main()
{
	int i,fa,x;
	cin>>n>>m;
	for(i=1;i<=n;i++)
	{
		scanf("%d%d%d",&fa,&x,&leadership[i]);
		if(!fa) root=i;
		else Add(fa,i);
		tree[i]=new abcd(x);
		sum[i]=x;
		size[i]=1;
	}
	Tree_DP(root);
	cout<<ans<<endl;
}
时间: 2024-10-08 08:01:08

BZOJ 2809 APIO2012 dispatching Treap+启发式合并 / 可并堆的相关文章

BZOJ 2809 APIO2012 dispatching Treap+启示式合并 / 可并堆

题目大意:给定一棵树,选定一棵子树中的一些点,薪水和不能超过m,求点的数量*子树根节点的领导能力的最大值 考虑对于每一个节点,我们维护一种数据结构,在当中贪心寻找薪金小的雇佣. 每一个节点暴力重建一定不行.我们考虑可并数据结构.每一个节点将子节点的信息直接合并就可以 能够用启示式合并的Treap.也能够用可并堆 今天特意去学了这玩应0.0 先写了左偏树 然后又写了下随机堆-- 后者速度上更快一些 只是建议从左偏树開始学起 总之平衡树常数各种大就是了0.0 Treap+启示式合并 #include

BZOJ 2809 [Apio2012]dispatching 可并堆

题意:链接 方法:可并堆 解析: 水题,但注意过程爆int. 方法就是找到树的根节点,之后扫,将每个子树什么的看做一个堆,然后之间合并,如果堆中的sum和超过了m,则去掉最大的,继续添加,这个显然啊. 然后每次处理完一个点,用堆中解更新答案. 比前两道水 代码: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 100100 usin

[BZOJ 2809][Apio2012]dispatching(左偏树)

Description 在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿.在这个帮派里,有一名忍者被称之为 Master.除了 Master以外,每名忍者都有且仅有一个上级.为保密,同时增强忍者们的领导力,所有与他们工作相关的指令总是由上级发送给他的直接下属,而不允许通过其他的方式发送.现在你要招募一批忍者,并把它们派遣给顾客.你需要为每个被派遣的忍者 支付一定的薪水,同时使得支付的薪水总额不超过你的预算.另外,为了发送指令,你需要选择一名忍者作为管理者,要求这个管理者

BZOJ 2809: [Apio2012]dispatching [主席树 DFS序]

传送门 题意:查询树上根节点值*子树中权值和$\le m$的最大数量 最大值是多少 求$DFS$序,然后变成区间中和$\le m$最多有几个元素,建主席树,然后权值线段树上二分就行了 $WA$:又把边表开小了..... 好吧我$zz$了有根树加无向边干什么.... #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #de

【BZOJ 2809】2809: [Apio2012]dispatching (左偏树)

2809: [Apio2012]dispatching Description 在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿.在这个帮派里,有一名忍者被称之为 Master.除了 Master以外,每名忍者都有且仅有一个上级.为保密,同时增强忍者们的领导力,所有与他们工作相关的指令总是由上级发送给他的直接下属,而不允许通过其他的方式发送.现在你要招募一批忍者,并把它们派遣给顾客.你需要为每个被派遣的忍者 支付一定的薪水,同时使得支付的薪水总额不超过你的预算.另外,为

BZOJ 2733: [HNOI2012]永无乡(treap + 启发式合并 + 并查集)

不难...treap + 启发式合并 + 并查集 搞搞就行了 ---------------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #define rep(i, n) for(int i = 0; i &l

BZOJ 2809 APIO 2012 dispatching 平衡树启发式合并

题目大意:给出一棵树,每一个节点有两个值,分别是这个忍者的薪水和忍者的领导力.客户的满意程度是这个点的领导力乘能够取得人数,前提是取的人的薪水总和不超过总的钱数. 思路:只能在子树中操作,贪心的想,我们只要这个子树中cost最小的那些点就可以了.所以就深搜一次,每到一个节点上,把自己和所有子节点的平衡树启发式和并,然后保留不超过总钱数的人数,统计.数据范围比较大,能开long long的地方不要吝啬. PS:吐槽一下,一开始这个题一直TTT,我以为是我常数写的太大了,别人都用左偏堆写,是不是平衡

BZOJ 2888 资源运输(启发式合并LCT)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2888 [题目大意] 不断加边,问每个连通块的重心到其它点的距离和的和 [题解] 启发式合并LCT,通过维护等差数列的首项和公差 来实现保存子树内所有节点到这个节点的距离之和. [代码] #include <cstdio> #include <algorithm> #include <cstring> using namespace std; const in

BZOJ.4298.[ONTAK2015]Bajtocja(Hash 启发式合并)

题目链接 \(Description\) 给定d张无向图,每张图都有n个点.一开始,在任何一张图中都没有任何边. 接下来有m次操作,每次操作会给出a,b,k,意为在第k张图中的点a和点b之间添加一条无向边. 你需要在每次操作之后输出有序数对(a,b)的个数,满足1≤a,b≤n,且a点和b点在d张图中都连通. d<=200,n<=5000,m<=1000000 \(Solution\) 我们需要知道的只是每对点之间是否连通,即在同一张图所属的连通块是否一样 于是我们对每个点在d张图中所属的