题目链接
给定一棵树,树上有m个宝箱,每个宝箱都有一个把钥匙,以及各自的价值,要先拿到钥匙才可以开宝箱,必须开掉所有能开的宝箱。要求选择一条简单路径,使得最后获得的总价值最大。
一开始各种想树上的算法,树分治什么的。
比赛后一看题解,居然是扫描线+线段树。
简直是到神(keng)题。
好吧这么考虑,首先利用dfs序,把树转到序列上。
然后对于每种情况,可以进行分类讨论。
令lca=LCA(A,B),假定A是宝箱所在的结点,B是钥匙所在的结点。
用一个点对(a,b)表示起点在dfn为a的点,终点在dfn为b的点。
如果起点在A的子树中,终点在B的子树中,那么取到这个宝箱.
由于不可能找出所有的经过A的点(那是 O(n2) 级的)。
正难则反。
先把这个宝箱的权值加到答案中去,然后在对于不经过这个点的路径减去这个点的权值。
这是又要分出三类
dfs序小于A的点连到dfs序大于A的点。
dfs序小于A点的连向dfs序小于A的点。
dfs序大于A点的连向dfs序大于A的点。
枚举所有A点的子树,然后子树内部相连。
这就是 O(n) 级别的了。
当然这样貌似会有极限数据,不过出题人不卡。
找到一个点,离A最近,记为C,那么起点在B的子树中,终点不在D的子树中的所有路径都可以拿到这个宝箱。
到这里似乎和扫描线没什么关系。
由于dfs在表示子树是都是连续的,那么就可以把以上这些情况用一些带权矩阵表示出来。
再把带权矩阵拆成两条线段。
扫描线+线段树就好了。
if(a==b){
ans+=c;
A[1]=1,B[1]=L[a]-1;
A[2]=R[a]+1,B[2]=n;
rep(j,0,edge[a].size()){
int to=edge[a][j];
if(to==pre[0][a]) continue;
add_matrix(L[to],R[to],L[to],R[to],-c);
}
rep(k,1,3)rep(j,1,3)
add_matrix(A[k],B[k],A[j],B[j],-c);
}else if(a==lca){
pointd=up(b,dep[b]-dep[a]-1);
add_matrix(L[b],R[b],1,L[pointd]-1,c);
add_matrix(L[b],R[b],R[pointd]+1,n,c);
}else if(b==lca){
pointd=up(a,dep[a]-dep[b]-1);
add_matrix(1,L[pointd]-1,L[a],R[a],c);
add_matrix(R[pointd]+1,n,L[a],R[a],c);
}else add_matrix(L[b],R[b],L[a],R[a],c);
vector<int>edge[M];
int dep[M],L[M],dfs_clock,pre[S][M],R[M];
int mx[M<<2],add[M<<2],pointd;
void build(int L,int R,int p){
add[p]=mx[p]=0;
if(L==R) return;
int mid=(L+R)>>1;
build(L,mid,p<<1);
build(mid+1,R,p<<1|1);
}
inline void down(int p){
if(!add[p]) return;
int l=p<<1,r=p<<1|1;
add[l]+=add[p];
add[r]+=add[p];
mx[l]+=add[p];
mx[r]+=add[p];
add[p]=0;
}
inline void up(int p){
mx[p]=max(mx[p<<1],mx[p<<1|1]);
}
void update(int L,int R,int w,int p,int l,int r){
if(L==l&&r==R){
add[p]+=w;
mx[p]+=w;
return;
}
down(p);
int mid=(l+r)>>1;
if(mid>=R) update(L,R,w,p<<1,l,mid);
else if(mid1|1,mid+1,r);
else update(L,mid,w,p<<1,l,mid),update(mid+1,R,w,p<<1|1,mid+1,r);
up(p);
}
void dfs(int x,int f){
L[x]=++dfs_clock;
dep[x]=dep[f]+1;
pre[0][x]=f;
rep(i,0,edge[x].size()){
int to=edge[x][i];
if(to==f) continue;
dfs(to,x);
}
R[x]=dfs_clock;
}
int up(int a,int step){
if(step<0) return a;
rep(i,0,S)if(step&(1<return a;
}
int LCA(int a,int b){
if(dep[a]if(a!=b){
per(i,0,S)if(pre[i][a]!=pre[i][b]) a=pre[i][a],b=pre[i][b];
a=pre[0][a];
}
return a;
}
struct Liner{
int x,a,b,c;
Liner(int _x=0,int _a=0,int _b=0,int _c=0):x(_x),a(_a),b(_b),c(_c){}
bool operator <(const Liner&tmp)const{
return x5];
int ltot;
inline void add_matrix(int a,int b,int c,int d,int e){
if(a>b||c>d) return;
lin[ltot++]=Liner(a,c,d,e);
lin[ltot++]=Liner(b+1,c,d,-e);
}
int A[4],B[4];
void solve(int cas){
rd(n);rd(m);
ltot=dfs_clock=0;
rep(i,1,n+1) edge[i].clear();
rep(i,1,n){
int a,b;
rd(a);rd(b);
edge[a].push_back(b);
edge[b].push_back(a);
}
dfs(1,0);
rep(j,1,S)rep(i,1,n+1)pre[j][i]=pre[j-1][pre[j-1][i]];
int ans=0;
rep(i,0,m){
int a,b,c;
rd(b);rd(a);
scanf("%d",&c);
int lca=LCA(a,b);
if(a==b){
ans+=c;
A[1]=1,B[1]=L[a]-1;
A[2]=R[a]+1,B[2]=n;
rep(j,0,edge[a].size()){
int to=edge[a][j];
if(to==pre[0][a]) continue;
add_matrix(L[to],R[to],L[to],R[to],-c);
}
rep(k,1,3)rep(j,1,3)
add_matrix(A[k],B[k],A[j],B[j],-c);
}else if(a==lca){
pointd=up(b,dep[b]-dep[a]-1);
add_matrix(L[b],R[b],1,L[pointd]-1,c);
add_matrix(L[b],R[b],R[pointd]+1,n,c);
}else if(b==lca){
pointd=up(a,dep[a]-dep[b]-1);
add_matrix(1,L[pointd]-1,L[a],R[a],c);
add_matrix(R[pointd]+1,n,L[a],R[a],c);
}else add_matrix(L[b],R[b],L[a],R[a],c);
}
sort(lin,lin+ltot);
build(1,n,1);
int res=-INF;
rep(i,0,ltot){
int R=i;
while(R+11].x==lin[i].x) R++;
rep(j,i,R+1) update(lin[j].a,lin[j].b,lin[j].c,1,1,n);
down(1);
res=max(res,mx[1]);
i=R;
}
printf("Case #%d: %d\n",cas,ans+res);
}
int main(){
int T;
rd(T);
for(int i=1;i<=T;i++) solve(i);
return 0;
}