[洛谷P1552] [APIO2012]派遣(左偏树)
标签:mil 工资 print 维护 tchar class ace queue getc
这道题是我做的左偏树的入门题,奈何还是看了zsy大佬的题解才能过,唉,我太弱了。
Part 1 理解题目
很显然,通过管理关系的不断连边,最后连出来的肯定是一棵树,那么不难得出,当一个忍者作为管理者时,最优解一定是去除掉所有的较大工资的忍者,剩下的忍者符合费用要求时,答案是管理者的管理能力×剩下的忍者数量。并且我们可以推出,当一棵子数中的一棵小子树中去掉了一个忍者,那么那个忍者一定不会对当前的子树有答案贡献。
Part 2 解题思想
都理解了题目了,就很清楚了,我们在dfs过程中记录一下集合元素,并把此节点以下所有的子树全部合并入一个堆里(dfs过程中已经处理了,每个堆都是最优堆),那么开始弹元素,维护大根堆,一个个弹出最大值,直到刚好符合要求,此时答案就是堆中剩余元素的数量×当前节点的管理能力,取max。完啦!
Part 3 code
#include
#include
#include
#include
#include
#include
#include
#include
#include#define rg register
#define lst long long
using namespace std;
#define ll long long
const int N = 100005;
struct edge{int to,next;}a[N];
int head[N],cnt;
int n,Master,ls[N],rs[N],dis[N];
ll m,C[N],L[N],sum[N],sz[N],ans;
ll gi()
{
ll x=0,w=1;char ch=getchar();
while ((ch‘0‘||ch>‘9‘)&&ch!=‘-‘) ch=getchar();
if (ch==‘-‘) w=0,ch=getchar();
while (ch>=‘0‘&&ch‘9‘) x=(x3)+(x1)+ch-‘0‘,ch=getchar();
return w?x:-x;
}
void Link(int u,int v)
{
a[++cnt]=(edge){v,head[u]};
head[u]=cnt;
}
int Merge(int A,int B)
{
if (!A||!B) return A+B;
if (C[A]C[B]) swap(A,B);
rs[A]=Merge(rs[A],B);
if (dis[ls[A]]dis[rs[A]]) swap(ls[A],rs[A]);
dis[A]=dis[rs[A]]+1;
return A;
}
int Delete(int A)
{
return Merge(ls[A],rs[A]);
}
int dfs(int u)
{
int A=u,B;
sum[u]=C[u];sz[u]=1;
for (int e=head[u];e;e=a[e].next)
{
int v=a[e].to;
B=dfs(v);
A=Merge(A,B);
sum[u]+=sum[v];sz[u]+=sz[v];
}
while (sum[u]>m)
{
sum[u]-=C[A];sz[u]--;
A=Delete(A);
}
ans=max(ans,L[u]*sz[u]);
return A;
}
int main()
{
n=gi();m=gi();
for (int i=1;i)
{
int u=gi();
if (!u) Master=i;
else Link(u,i);
C[i]=gi();L[i]=gi();
}
dfs(Master);
printf("%lld",ans);
return 0;
}
[洛谷P1552] [APIO2012]派遣(左偏树)
标签:mil 工资 print 维护 tchar class ace queue getc
原文地址:https://www.cnblogs.com/cjoierljl/p/8641019.html
评论