P1552 [APIO2012]派遣
思路
复习下左偏树
左偏树首先满足堆性质,其次定义了一个dis,使得每个点的dis都是右子树dis+1,且左子树的dis>=右子树的dis
合并过程可以参考FHQ treap的merge进行修改
一开始看错题了,是选择的人要能跟所有派遣者通信(等价于是选择子树中的点),不是选中一个人使他变得可以与所有人通信
观察到代价之和领导力和选择的人的数量最多,所以派出人应当数量最多,用大根堆维护这个性质,然后在枚举每个人当作首领的情况,把其他子树合并过来之后继续维护选出数量最多且总和小于m的性质,然后更新ans即可
记得开longlong
代码
#include <cstdio>
#include <algorithm>
#include <cstring>
#define int long long
using namespace std;
struct Node{
int num,lson,rson,dis,sum,sz,fa;
}LT[100100];
int fa[100100],c[100100],L[100100],n,m,ans,root;
int u[100100<<1],v[100100<<1],fir[100100],nxt[100100<<1],cnt;
void addedge(int ui,int vi){
++cnt;
u[cnt]=ui;
v[cnt]=vi;
nxt[cnt]=fir[ui];
fir[ui]=cnt;
}
void init(void){
for(int i=1;i<=n;i++){
LT[i].num=LT[i].sum=c[i];
LT[i].sz=1;
LT[i].fa=i;
LT[i].lson=LT[i].rson=0;
LT[i].dis=1;
}
}
void pushup(int o){
LT[o].sum=LT[LT[o].lson].sum+LT[LT[o].rson].sum+LT[o].num;
LT[o].sz=LT[LT[o].lson].sz+LT[LT[o].rson].sz+1;
}
int findroot(int o){
if(LT[o].fa==o)
return o;
return LT[o].fa=findroot(LT[o].fa);
}
int merge(int x,int y){
if((!x)||(!y))
return x+y;
if(LT[x].num<LT[y].num)
swap(x,y);
LT[x].rson=merge(LT[x].rson,y);
if(LT[LT[x].lson].dis<LT[LT[x].rson].dis)
swap(LT[x].lson,LT[x].rson);
LT[x].dis=LT[LT[x].rson].dis+1;
LT[LT[x].lson].fa=LT[LT[x].rson].fa=LT[x].fa=x;
pushup(x);
return x;
}
int top(int x){
return LT[findroot(x)].num;
}
void pop(int x){
LT[LT[x].lson].fa=LT[x].lson;
LT[LT[x].rson].fa=LT[x].rson;
LT[x].fa=merge(LT[x].lson,LT[x].rson);
}
void dfs(int x){
for(int i=fir[x];i;i=nxt[i]){
if(v[i]==fa[x])
continue;
dfs(v[i]);
merge(findroot(x),findroot(v[i]));
}
while(LT[findroot(x)].sum>m)
pop(findroot(x));
ans=max(ans,LT[findroot(x)].sz*L[x]);
}
signed main(){
scanf("%lld %lld",&n,&m);
for(int i=1;i<=n;i++){
scanf("%lld %lld %lld",&fa[i],&c[i],&L[i]);
if(fa[i]==0)
root=i;
else{
addedge(fa[i],i);
addedge(i,fa[i]);
}
}
init();
dfs(root);
printf("%lld\n",ans);
return 0;
}