題目大意:
給定一棵有 n 個點的樹,以及 m 條樹鏈,其中第 i 條樹鏈 的價值爲 wi,請選擇一些沒有公共點的樹鏈,使得價值和最大。
1 ≤n,m≤ 100000。
Source:2015 Multi-University Training Contest 1
分析:
- 設f[u]爲u爲根的子樹上選擇沒有公共點的樹鏈所得的最大價值和。
- 轉移方程:
枚舉鏈的兩端點的lca爲u的鏈,記w爲當前鏈的價值
f[u]=max{∑f[u.son],w+∑k爲當前鏈上的節點的兒子f[k]} 時間複雜度O((n+m)*n),過不了,選擇優化,記sum[u]爲u的所有兒子v的f[v]之和,得到:
f[u]=max{∑f[u.son],w+∑j爲當前鏈上的節點sum[j]−f[j]}
(鏈上的點會在它的父親的sum[]中多加,要再減去)可根據dfs序維護樹狀數組來實現優化。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define MAXN 100000
#define MAXM 100000
#define MAXLOG 20
typedef long long LL;
struct node{
int v;
node *next;
}edge[MAXN*2+10],*adj[MAXN+10],*ecnt=&edge[0];
vector<int> pnt[MAXN+10];
int n,m,LogN,chain[MAXM+10][3];
int fa[MAXN+10],dep[MAXN+10],P[MAXN+10][MAXLOG+10];
int in[MAXN+10],out[MAXN+10],cntd;
LL c[2*MAXN+10],f[MAXN+10];
void Init()
{
memset(adj,0,sizeof adj);
ecnt=&edge[0];
memset(pnt,0,sizeof pnt);
memset(fa,0,sizeof fa);
memset(dep,0,sizeof dep);
memset(f,0,sizeof f);
cntd=0;
memset(c,0,sizeof c);
}
void addedge(int u,int v)
{
node *p=++ecnt;
p->v=v;
p->next=adj[u];
adj[u]=p;
}
void dfs(int u,int prev,int L)
{
fa[u]=prev;
dep[u]=L;
for(node *p=adj[u];p;p=p->next){
if(p->v==fa[u]) continue;
dfs(p->v,u,L+1);
}
}
void LCA_pre()
{
memset(P,-1,sizeof P);
for(int i=1;i<=n;i++)
P[i][0]=fa[i];
for(LogN=0;(1<<LogN)<=n;LogN++);
LogN--;
for(int j=1;j<=LogN;j++){
for(int i=1;i<=n;i++)
if(P[i][j-1]!=-1)
P[i][j]=P[P[i][j-1]][j-1];
}
}
int LCA(int x,int y)
{
if(dep[x]<dep[y])
swap(x,y);
int Logx;
for(Logx=0;(1<<Logx)<=dep[x];Logx++);
Logx--;
for(int i=Logx;i>=0;i--)
if(dep[P[x][i]]>=dep[y])
x=P[x][i];
if(x==y) return x;
for(int i=Logx;i>=0;i--)
if(P[x][i]!=P[y][i])
x=P[x][i],y=P[y][i];
return fa[x];
}
void read()
{
int x,y;
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++){
scanf("%d%d",&x,&y);
addedge(x,y);
addedge(y,x);
}
dfs(1,0,1);
LCA_pre();
for(int i=1;i<=m;i++){
scanf("%d%d%d",&chain[i][0],&chain[i][1],&chain[i][2]);
int lca=LCA(chain[i][0],chain[i][1]);
pnt[lca].push_back(i);
}
}
int lowbit(int x){
return x&(-x);
}
void Update(int x,LL d){
while(x<=2*n){
c[x]+=d;
x+=lowbit(x);
}
}
LL Getsum(int x){
LL ret=0;
while(x){
ret+=c[x];
x-=lowbit(x);
}
return ret;
}
void DP(int u) //dfs for dp on the tree
{
LL g_u=0;
in[u]=++cntd;
for(node *p=adj[u];p;p=p->next){
if(p->v==fa[u]) continue;
DP(p->v);
g_u+=f[p->v];
}
out[u]=++cntd;
Update(in[u],g_u);
Update(out[u]+1,-g_u);
f[u]=g_u;
int side=pnt[u].size();
for(int i=0,num;i<side;i++){
num=pnt[u][i];
f[u]=max(f[u],Getsum(out[chain[num][0]])+Getsum(out[chain[num][1]])+1LL*chain[num][2]-g_u);
}
Update(in[u],-f[u]);
Update(out[u]+1,f[u]);
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
Init();
read();
DP(1);
printf("%I64d\n",f[1]);
}
}