线段树二分+最优解和次优解处理
题目
因为两人都足够聪明,所以V肯定会剪掉最优解,因此除了根以外,其他点都要剪掉最优解。同时,M只能选择一条路径来走,考虑在每个点都当作路径的终点,每次快速的统计一下答案。M肯定是在花费少的地方买的越多越好,要动态维护这个路径的变化,考虑用线段树来维护前缀和。每次用子树的次优解来更新当前解。
线段树用花费来进行下标建树,每次尽快选靠近左边的点
#include<bits/stdc++.h>
#define pii pair<int,int>
#define ll long long
#define int long long
using namespace std;
const int N = 1e5+100,M=1e6+10;
vector<pii>g[N];
ll nx[N],tx[N];
ll mx[N][2];
int rt,tot;
struct T{
int lc[M<<2],rc[M<<2];
ll sum[M<<2],num[M<<2];
//以时间为下标进行线段树建树
void update(int &c,int l,int r,ll pos,int k){
if(!c) c=++tot;
if(l==r){
num[c]+=k;
sum[c]+=pos*k;
return;
}
ll mid = l+r>>1;
if(pos<=mid) update(lc[c],l,mid,pos,k);
else update(rc[c],mid+1,r,pos,k);
sum[c]=sum[lc[c]]+sum[rc[c]];
num[c]=num[lc[c]]+num[rc[c]];
}
ll query(int c,int l,int r,ll t){
if(!rt) return 0;
if(sum[c]<=t) return num[c];
if(l==r){
return t/l;
}
int mid =l+r>>1;
if(sum[lc[c]]<=t){
return num[lc[c]]+query(rc[c],mid+1,r,t-sum[lc[c]]);
}
else return query(lc[c],l,mid,t);
}
}b;
ll res,ans[N];
void dfs(int u,ll T){
b.update(rt,1,1e6,tx[u],nx[u]);
ans[u]=b.query(rt,1,1e6,T);
for(auto[to,w]:g[u]){
if(T-2*w>0) dfs(to,T-2*w);
else continue;
if(mx[u][0]>ans[to]) mx[u][1]=max(ans[to],mx[u][1]);
else mx[u][1]=mx[u][0],mx[u][0]=ans[to];
}
if(u==1) ans[u]=max(ans[u],mx[u][0]);
else ans[u]=max(ans[u],mx[u][1]);
b.update(rt,1,1e6,tx[u],-nx[u]);
}
signed main(){
ll n,T;
cin>>n>>T;
for(int i=1;i<=n;i++){
cin>>nx[i];
}
for(int i=1;i<=n;i++){
cin>>tx[i];
}
for(int i=2;i<=n;i++){
int f,l;
cin>>f>>l;
g[f].push_back({i,l});
}
dfs(1,T);
cout<<ans[1]<<'\n';
}