令
fi
表示子树
i
中加边的方案,
若当前考虑的点为
u
,第一种方案是不加边,那么
如果要加边,那么加的边的两个端点的lca肯定是 u
如果加一条
考虑一个点的权值 wi=gi−fi 用dfs加树状数组就可以维护出这个点到根路径的权值和。
O(nlogn)
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <set>
#include <queue>
#include <assert.h>
#include <cstring>
#include <tuple>
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int> par;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
template<class T> inline void rea(T &x){
char c=nc(); x=0;
for(;c>'9'||c<'0';c=nc());for(;c>='0'&&c<='9';x=x*10+c-'0',c=nc());
}
inline int rea(char *x,int L='a',int R='z'){
char c=nc(); int len=0;
for(;c>R||c<L;c=nc());for(;c>=L&&c<=R;x[++len]=c,c=nc()); return len;
}
const int N=200010,P=1e9+7;
int n,m,it,L[N],R[N],dpt[N];
vector<int> s[N];
struct iedge{
int x,y,w;
iedge(int _x=0,int _y=0,int _w=0):x(_x),y(_y),w(_w){}
};
vector<iedge> e[N];
int fa[N][20];
void dfs(int u){
L[u]=++it; dpt[u]=dpt[fa[u][0]]+1;
for(int i=1;i<=18;i++) fa[u][i]=fa[fa[u][i-1]][i-1];
for(int v : s[u]) dfs(v);
R[u]=it;
}
inline int lca(int x,int y){
if(dpt[x]<dpt[y]) swap(x,y);
for(int i=18;~i;i--)
if(dpt[fa[x][i]]>=dpt[y]) x=fa[x][i];
if(x==y) return x;
for(int i=18;~i;i--)
if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int f[N],b[N];
inline void Add(int x,int y){
for(;x<=n;x+=x&-x) b[x]+=y;
}
inline int Query(int x){
int ret=0;
for(;x;x-=x&-x) ret+=b[x];
return ret;
}
void dp(int u){
for(int v : s[u]) dp(v);
int ss=0;
for(int v : s[u]) ss+=f[v];
f[u]=ss;
for(auto ed : e[u]){
int cur=Query(L[ed.x])+Query(L[ed.y])+ed.w+ss;
f[u]=max(cur,f[u]);
}
Add(L[u],ss-f[u]); Add(R[u]+1,f[u]-ss);
}
int main(){
rea(n); rea(m);
for(int i=2,x;i<=n;i++)
rea(x),s[x].push_back(i),fa[i][0]=x;
dfs(1);
for(int i=1,x,y,w;i<=m;i++)
rea(x),rea(y),rea(w),e[lca(x,y)].push_back({x,y,w});
dp(1);
printf("%d\n",f[1]);
return 0;
}