传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2809
题意就是给定一棵树,每个节点有两个属性C(薪水),L(领导力),找出一个节点u和u的子节点S(sigma S.C <=m)使得 u.L*|S|最大
显然根结点需要枚举,看看数据……n<=1e5 看来要logn 求满足条件的最多的子节点了……蒟蒻首先想到了背包,然后发现我是SX……
因为价值都是1,所以很明显从小到大选人最优 = =、于是就是求以某树为根节点的子节点中从小到大的前缀和<=m的最大个数(描述渣……)
于是蒟蒻想到了dfs序+树套树or主席树 然后不会写……QAQ 我好弱啊啊啊啊啊
再仔细想想,如果在同一个根结点下,A的C比B的C大,那么选A不可能比选B更优,在不能选取时应优先去除A,这就与堆有关,而且计算根结点的值需要合并其所有子树所形成的堆,可合并堆?那就左偏树吧!
dfs自底向上枚举节点作为管理员,每当sum>m的时候就pop堆中最大元素,子节点枚举完成后就合并成根节点,边走边记录ans的最大值,然后就完了,代码不长,应该很容易读懂
Code:
#include<cstdio>
#include<vector>
#include<climits>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e5+10;
int n,m;
int sor[maxn],lead[maxn],tot;
struct node{
int val,dis;
node *c[2];
}*Null,poor[maxn<<2];
namespace TRT{
struct LeftistTree{
node *root;
int sum,size;
void init(){
sum=size=0;
root=Null;
}
node *merge(node *&A,node *&B){
if(A==Null)return B;
if(B==Null)return A;
if(A->val<B->val)
swap(A,B);
A->c[1]=merge(A->c[1],B);
if(A->c[0]->dis<A->c[1]->dis)
swap(A->c[0],A->c[1]);
A->dis=A->c[1]->dis+1;
return A;
}
void merge(LeftistTree &B){
size+=B.size;
sum+=B.sum;
root=merge(root,B.root);
}
void insert(int x) {
node *p = &poor[++tot];
++size;
sum += x;
p->val = x;
p->dis = 0;
p->c[0] = p->c[1] = Null;
root = merge(root, p);
}
int top(){
return root->val;
}
void pop(){
size--;
sum-=root->val;
root=merge(root->c[0],root->c[1]);
}
}t[maxn];
}
using namespace TRT;
namespace TOPL{
vector<int>G[maxn];
void add(int u,int v){
G[u].push_back(v);
}
int root=1;
void init(){
Null=new node;Null->dis=-1;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
int a,b,c;scanf("%d%d%d",&a,&b,&c);
if(a)add(a,i);
else root=i;
sor[i]=b;
lead[i]=c;
}
}
long long ans=0;
void dfs(int u){
t[u].init();
t[u].insert(sor[u]);
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
dfs(v);
t[u].merge(t[v]);
while(t[u].sum>m)
t[u].pop();
}
ans=max(ans,(long long)lead[u]*t[u].size);
}
void solve(){
init();
dfs(root);
cout<<ans<<endl;
}
}
int main(){
TOPL::solve();
return 0;
}