给定一棵树,树上有m个宝箱,每个宝箱都有一个把钥匙,以及各自的价值,要先拿到钥匙才可以开宝箱,必须开掉所有能开的宝箱。要求选择一条简单路径,使得最后获得的总价值最大。
官方题解:
假设钥匙在节点 A ,宝箱在节点
B , C=LCA(A,B) ,则可以分四种情况讨论1. C≠A,C≠B 对于这种情况,只要起点在以 A 为根的子树中,终点在以
B 为根的子树中,都可以拿到这份宝藏,而子树 A 中的所有节点dfs序连续,子树B 同理,于是我们可以用一个矩阵表示能取得该宝藏的所有方案。2. C=A,A≠B 对于这种情况,需要先求出节点 D ,
D 为路径 (A,B) 上最靠近 A 的节点,那么只要终点在子树B 上,起点不在子树 D 上的路径,都可以拿到这份宝藏,而不在子树D 上的点,可以用一个或者两个dfs序区间表示,因此可以用最多两个矩阵表示能取得该宝藏的所有方案。3. C=B,A≠B 和情况2类似。
4. A=B 对于这种情况,若要求出所有经过节点 A 的路径,矩阵数目会是
n2 级别的,因此反过来思考,求出所有不包含节点 A 的路径,对于全部这种情况来说这样矩阵数目的级别为n 。可以对答案先累加宝藏权值,然后对于所有不经过该点的矩阵减去这部分权值即可。
dfs序+扫描线(线段树)
#include<set>
#include<ctime>
#include<queue>
#include<cstdio>
#include<bitset>
#include<cctype>
#include<bitset>
#include<cstdlib>
#include<cassert>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf (1<<30)
#define INF (1ll<<62)
#define fi first
#define se second
#define rep(x,s,t) for(int x=s,t_=t;x<t_;x++)
#define per(x,s,t) for(int x=t-1,s_=s;x>=s_;x--)
#define prt(x) cout<<#x<<":"<<x<<" "
#define prtn(x) cout<<#x<<":"<<x<<endl
#define pc(x) putchar(x)
#define pb(x) push_back(x)
#define hash asfmaljkg
#define rank asfjhgskjf
#define y1 asggnja
#define y2 slfvm
using namespace std;
typedef long long ll;
typedef pair<int,int> ii;
template<class T>void sc(T &x){
int f=1;char c;x=0;
while(c=getchar(),c<48)if(c=='-')f=-1;
do x=x*10+(c^48);
while(c=getchar(),c>47);
x*=f;
}
template<class T>void nt(T x){
if(!x)return;
nt(x/10);
pc(x%10+'0');
}
template<class T>void pt(T x){
if(x<0)pc('-'),x=-x;
if(!x)pc('0');
else nt(x);
}
template<class T>void ptn(T x){
pt(x);putchar('\n');
}
template<class T>void pts(T x){
pt(x);putchar(' ');
}
template<class T>inline void Max(T &x,T y){if(x<y)x=y;}
template<class T>inline void Min(T &x,T y){if(x>y)x=y;}
const int maxn=100005;
int n,m;
int lx[maxn],rx[maxn],dfs_clock;
int link[maxn];
int last[maxn],ecnt;
struct Edge{
int to,nxt;
}e[maxn<<1];
inline void ins(int u,int v){
e[ecnt]=(Edge){v,last[u]};
last[u]=ecnt++;
}
int tot;
struct unit{
int x,l,r,v;
inline bool operator<(const unit&a)const{
return x<a.x;
}
}tar[maxn*3];
int sz[maxn],par[maxn],dep[maxn];
void assign(int x,int f){
sz[x]=1;
par[x]=f;
dep[x]=dep[f]+1;
for(int i=last[x];i!=-1;i=e[i].nxt){
int to=e[i].to;
if(to==f)continue;
assign(to,x);
sz[x]+=sz[to];
}
}
void dfs(int x,int p){
int mx=0;
link[x]=p;
lx[x]=rx[x]=++dfs_clock;
for(int i=last[x];i!=-1;i=e[i].nxt){
int to=e[i].to;
if(to==par[x])continue;
if(sz[to]>sz[mx])mx=to;
}
if(!mx)return;
dfs(mx,p);
for(int i=last[x];i!=-1;i=e[i].nxt){
int to=e[i].to;
if(to==par[x])continue;
if(to!=mx)dfs(to,to);
}
rx[x]=dfs_clock;
}
int lca(int x,int y){
while(link[x]!=link[y]){
if(dep[link[x]]<dep[link[y]])swap(x,y);
x=par[link[x]];
}
return dep[x]<dep[y]?x:y;
}
void add_rec(int a1,int a2,int b1,int b2,int val){
// [a1,a2][b1,b2]
// prt(a1);prt(a2);prt(b1);prt(b2);prtn(val);
tar[++tot]=(unit){a1,b1,b2,val};
// prtn(tar[tot].r);
tar[++tot]=(unit){a2+1,b1,b2,-val};
// prtn(tar[tot].r);
}
struct node{
int mx,flag;
};
struct Segment{
node v[maxn<<2];
void down(int x){
if(v[x].flag){
adjust(x<<1,v[x].flag);
adjust(x<<1|1,v[x].flag);
v[x].flag=0;
}
}
inline void up(int x){
v[x].mx=max(v[x<<1].mx,v[x<<1|1].mx);
}
inline void adjust(int x,int flag){
v[x].mx+=flag;
v[x].flag+=flag;
}
void update(int l,int r,int x,int L,int R,int val){
if(L<=l&&r<=R){
adjust(x,val);
return;
}
int mid=l+r>>1;
down(x);
if(L<=mid)update(l,mid,x<<1,L,R,val);
if(R>mid)update(mid+1,r,x<<1|1,L,R,val);
up(x);
}
void build(int l,int r,int x){
v[x].mx=v[x].flag=0;
if(l==r)return;
int mid=l+r>>1;
build(l,mid,x<<1);
build(mid+1,r,x<<1|1);
}
int maximum(){
return v[1].mx;
}
}sgm;
void init(){
memset(last,-1,n+1<<2);
ecnt=0;
dfs_clock=0;
tot=0;
sgm.build(1,n,1);
}
int all;
#define get got
int get(int x,int y){
for(int i=last[x];i!=-1;i=e[i].nxt){
int to=e[i].to;
if(to==par[x])continue;
if(lx[to]<=lx[y]&&rx[y]<=rx[to])
return to;
}
return 0;
}
void update(int u,int v,int x){
int w=lca(u,v);
if(w!=u&&w!=v)
add_rec(lx[u],rx[u],lx[v],rx[v],x);
else if(w!=u){//w==v
int k=get(v,u);
if(rx[k]<n)add_rec(lx[u],rx[u],rx[k]+1,n,x);
if(lx[k]>1)add_rec(lx[u],rx[u],1,lx[k]-1,x);
}
else if(w!=v){//w==u
int k=get(u,v);
if(rx[k]<n)add_rec(rx[k]+1,n,lx[v],rx[v],x);
if(lx[k]>1)add_rec(1,lx[k]-1,lx[v],rx[v],x);
}
else{
all+=x;
for(int i=last[u];i!=-1;i=e[i].nxt){
int to=e[i].to;
if(to==par[u])continue;
add_rec(lx[to],rx[to],lx[to],rx[to],-x);
}
if(lx[u]>1)add_rec(1,lx[u]-1,1,lx[u]-1,-x);
if(rx[u]<n)add_rec(rx[u]+1,n,rx[u]+1,n,-x);
if(lx[u]>1&&rx[u]<n){
add_rec(1,lx[u]-1,rx[u]+1,n,-x);
add_rec(rx[u]+1,n,1,lx[u]-1,-x);
}
}
}
int ans;
void work(){
ans=-inf;
sort(tar+1,tar+tot+1);
int top=1;
rep(i,1,n+1){
while(top<=tot&&tar[top].x==i){
int l=tar[top].l;
int r=tar[top].r;
int v=tar[top].v;
sgm.update(1,n,1,l,r,v);
top++;
}
Max(ans,sgm.maximum());
}
}
void solve(){
sc(n);sc(m);
init();
int u,v;
rep(i,1,n){
sc(u);sc(v);
ins(u,v);
ins(v,u);
}
assign(1,0);
dfs(1,1);
int w;
all=0;
rep(i,0,m){
sc(u);sc(v);sc(w);
update(u,v,w);
}
work();
ptn(ans+all);
}
int main(){
// freopen("pro.in","r",stdin);
// freopen("pro.out","w",stdout);
int cas;sc(cas);
rep(kase,1,cas+1){
printf("Case #%d: ",kase);
solve();
}
return 0;
}