[APIO2012] 派遣
题意
给定一个m和一棵树,树上每个点有两个权值w,c。
需要选择一个节点r,然后从这个节点的子树中选择一些点,这些点满足如下条件:
$ \sum{w_i} \leq m $
并且使 $ c_r \times select\_node\_num $ 最大
节点数 $ n\leq 100,000$
解法
暴力的想法就是枚举节点,然后把所有子节点按权值排序,然后取尽可能多的点就好了
然后稍微一想,其实可以先从叶子节点做起,然后一层一层的往上合并
但是难点在于没办法快速把每个子树的序列合并起来,如果这个树是二叉树,那用归并的话,只要让序列的和不超过m,然后一直合并,复杂度应该是可以保证的,但是这个题的子树可以很多,因此时间复杂度应该会爆
反向思考一下,整个大根堆,当堆内元素的和大于m的时候就pop,答案就是堆的大小和根的权值乘积,那么为了快速合并,使用可并堆就好了
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define maxn (100000+5)
#define mod 1000000007
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();}
return x*f;
}
ll readll(){
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();}
return x*f;
}
int gcd(int x,int y){return y?gcd(y,x%y):x;}
int power(int x,int y){
int t=1;
for(;y;y>>=1,x=x*x%mod)
if(y&1)t=t*x%mod;
return t;
}
int n;
ll m;
ll c[maxn];
vector<vector<int> > edge(maxn, vector<int>(0));
struct Node{
int ch[2],d;
int size;
ll val,sum;
} t[maxn];
int& rs(int x){return t[x].ch[t[t[x].ch[1]].d < t[t[x].ch[0]].d];}
int merge(int x,int y){
if(!x || !y) return x|y;
if(t[x].val > t[y].val) swap(x,y);
rs(x) = merge(rs(x), y);
t[x].sum = t[t[x].ch[0]].sum + t[t[x].ch[1]].sum + t[x].val;
t[x].size = 1 + t[t[x].ch[0]].size + t[t[x].ch[1]].size;
t[x].d = t[rs(x)].d + 1;
return x;
}
int pop(int x){
return merge(t[x].ch[0], t[x].ch[1]);
}
ll ans = 0;
int dfs(int x){
if(edge[x].size() == 0){
ans = max(ans, -t[x].val>m?0:c[x]);
}
int treeRoot = x;
for(int i=0;i<edge[x].size();i++){
treeRoot = merge(dfs(edge[x][i]), treeRoot);
while(-t[treeRoot].sum > m){
treeRoot = pop(treeRoot);
}
ans = max(ans, c[x]*t[treeRoot].size);
}
return treeRoot;
}
void solve(){
n=read();m=readll();
int root=0;
for(int i=1;i<=n;i++){
int fa = read();
t[i].val = t[i].sum = -readll();
t[i].size=1;
c[i]=readll();
if(!fa){
root = i;
}
else{
edge[fa].push_back(i);
}
}
dfs(root);
cout<<ans<<endl;
}
int main(){
//freopen("input.txt","r",stdin);
//freopen("output.txt","w",stdout);
solve();
return 0;
}