题解:
发现实际上把每个儿子第一次到达祖先的时间点拿出来排个序,祖先单位时间受到的伤害其实是个分段一次函数。总伤害就是求前缀和。
需要维护排好序的儿子到祖先的时间,dsu on tree,每次计算的时候二分答案并check,没了。。。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
namespace IO{
inline char gc(){
static cs int Rlen=1<<22|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline T get(){
char c;T num;
while(!isdigit(c=gc()));num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
inline int gi(){return get<int>();}
}
using namespace IO;
using std::cerr;
using std::cout;
typedef std::pair<ll,int> pli;
#define fi first
#define se second
cs int N=1e5+7;
int lc[N],rc[N],siz[N],tot;
ll sum[N],val[N];
int del[N],tp;
int q[N],qn;
inline int newnode(int v){
int u=tp?del[tp--]:++tot;
lc[u]=rc[u]=0;
siz[u]=1,sum[u]=val[u]=v;
return u;
}
inline void pushup(int u){
siz[u]=siz[lc[u]]+siz[rc[u]]+1;
sum[u]=sum[lc[u]]+sum[rc[u]]+val[u];
}
inline void Zig(int &u){
int v=lc[u];lc[u]=rc[v];rc[v]=u;
pushup(u),pushup(v);u=v;
}
inline void Zag(int &u){
int v=rc[u];rc[u]=lc[v];lc[v]=u;
pushup(u),pushup(v);u=v;
}
cs double alpha=0.65;
void ins(int &u,int v){
if(!u){u=newnode(v);return ;}
siz[u]++;sum[u]+=v;
if(v<=val[u]){ins(lc[u],v);if(siz[lc[u]]>=siz[u]*alpha)Zig(u);}
else {ins(rc[u],v);if(siz[rc[u]]>=siz[u]*alpha)Zag(u);}
}
void inorder_dfs(int u){
if(lc[u])inorder_dfs(lc[u]);
q[++qn]=val[u];del[++tp]=u;
if(rc[u])inorder_dfs(rc[u]);
}
ll query(int u,int v){
if(!u)return 0;
if(val[u]>v)return query(lc[u],v);
else return (siz[lc[u]]+1)*(ll)v-sum[lc[u]]-val[u]+query(rc[u],v);
}
int n,d[N];
int el[N],nxt[N<<1],to[N<<1],w[N<<1],ect;
inline void adde(int u,int v,int val){
nxt[++ect]=el[u],el[u]=ect,to[ect]=v,w[ect]=val;
nxt[++ect]=el[v],el[v]=ect,to[ect]=u,w[ect]=val;
}
int sz[N],son[N],to_son[N];
void dfs1(int u,int p){
sz[u]=1;for(int re e=el[u],v=to[e];e;v=to[e=nxt[e]])
if(v!=p){dfs1(v,u),sz[u]+=sz[v];if(sz[v]>sz[son[u]])son[u]=v,to_son[u]=w[e];}
}
int rt[N],tag[N],Ans;
inline void Join(int u,int v,int w){
//由于已经轻重链剖分,直接将v并到u上同时处理标记
//不然还要启发式合并考虑交换两个根。。。感觉标记处理要分类讨论,不想分类讨论QAQ
qn=0;inorder_dfs(rt[v]);w+=tag[v]-tag[u];
for(int re i=1;i<=qn;++i)ins(rt[u],q[i]+w);
}
inline bool check(int u,int t){
return query(rt[u],t)>=d[u];
}
inline void solve(int u){
if(!rt[u])ins(rt[u],0);
else {
int l=-tag[u]-7,r=1e8+7,mid,ans=0;//下界必须足够低,因为最后答案加上tag[u],当 d[u] 过小的时候这个二分就gg
while(l<=r)check(u,mid=(l+r)/2)?r=mid-1,ans=mid:l=mid+1;
// cerr<<"solve : "<<u<<" "<<ans+tag[u]<<"\n";
Ans=std::max(Ans,ans+tag[u]);ins(rt[u],ans);
}
}
void dfs2(int u,int p){
if(son[u])dfs2(son[u],u),rt[u]=rt[son[u]],tag[u]=tag[son[u]]+to_son[u];
for(int re e=el[u],v=to[e];e;v=to[e=nxt[e]])
if(v!=p&&v!=son[u]){dfs2(v,u);Join(u,v,w[e]);}
solve(u);
}
signed main(){
#ifdef zxyoi
freopen("conquer.in","r",stdin);
#else
#ifndef ONLINE_JUDGE
freopen("conquer.in","r",stdin);freopen("conquer.out","w",stdout);
#endif
#endif
n=gi();
for(int re i=1;i<=n;++i)d[i]=gi();
for(int re i=1;i<n;++i){
int u=gi(),v=gi(),w=gi();
adde(u,v,w);
}
dfs1(1,0);dfs2(1,0);cout<<Ans<<"\n";
return 0;
}
转载:https://blog.csdn.net/zxyoi_dreamer/article/details/101997790
查看评论