题意:
树上n个点,点有黑有白,一条路径上黑点个数不超过k个的最大权值是多少
思路:
从点分治上想,我们每次统计通过某个点的,符合条件的路径最大权值,然后不停更新答案。当我们遍历完某个子树,就把这个子树的信息和之前子树合并,然后不停更新即可。很明显,我们要维护当经过黑点为x个时的最大权值。
假设我们在遍历某棵子树的时候,得到了当黑点个数为x个的最大权值,那么在之前已经遍历过的子树中,0~k-x(如果根为黑则还要减一)个黑点的权值都是可行的,这里可以用线段树暴力维护,这样复杂度为
,这样很暴力,也很好想,然而会TLE。
考虑如何
的方法,首先我们发现,我们维护的最大权值,其实可以等价为一个非严格递增的数组(因为1个黑点的答案可以,那么我0个黑点的也自然可以),如果我们暴力地已知遍历数组维护,最差达到
(比如:我第一个子树大小为n/2,后面子树每个大小为1,那么一共就要遍历
次),考虑如何减少这个次数,我们发现,子树遍历顺序不影响最终答案,那么我们可以处理出子树最大黑点个数,利用这个排序,这样就能
地暴力更新那个数组了(这个东西就是启发式合并。。。)
错误及反思:
把j写成now,因为这个找了一天bug,整个人都不好了。。。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 200005;
struct E{
int to,next;
int val;
}e[N*2];
struct D{
int to,val,deep;
};
bool cmp(D a,D b){return a.deep<b.deep;}
int first[N],n,k,m,tot=0,si[N],max_si[N],ans=0,f[N],tmp[N];
bool did[N],black[N];
vector<D> v;
void addedge(int x,int y,int z){
e[tot].to=y; e[tot].val=z; e[tot].next=first[x]; first[x]=tot++;
e[tot].to=x; e[tot].val=z; e[tot].next=first[y]; first[y]=tot++;
}
void dfs_size(int now,int fa){
si[now]=1;
max_si[now]=0;
for(int i=first[now];i!=-1;i=e[i].next)
if(e[i].to!=fa&&!did[e[i].to]){
dfs_size(e[i].to,now);
si[now]+=si[e[i].to];
max_si[now]=max(max_si[now],si[e[i].to]);
}
}
void dfs_root(int now,int fa,int& root,int& maxsize,int t){
int tmax=max(max_si[now],si[t]-si[now]);
if(tmax<maxsize){
maxsize=tmax;
root=now;
}
for(int i=first[now];i!=-1;i=e[i].next)
if(e[i].to!=fa&&!did[e[i].to])
dfs_root(e[i].to,now,root,maxsize,t);
}
void dfs_subtree(int now,int fa,int num,int val){
for(int i=first[now];i!=-1;i=e[i].next)
if(e[i].to!=fa&&!did[e[i].to]){
if(black[e[i].to]) dfs_subtree(e[i].to,now,num+1,val+e[i].val);
else dfs_subtree(e[i].to,now,num,val+e[i].val);
}
tmp[num]=max(tmp[num],val);
}
void dfs_deep(int now,int fa,int dep,int &maxn){
if(dep>k) return ;
maxn=max(maxn,dep);
for(int i=first[now];i!=-1;i=e[i].next)
if(e[i].to!=fa&&!did[e[i].to]){
if(black[e[i].to]) dfs_deep(e[i].to,now,dep+1,maxn);
else dfs_deep(e[i].to,now,dep,maxn);
}
}
void solve(int now){
int root,maxsize=1e9,td=0;
v.clear();
dfs_size(now,-1);
dfs_root(now,-1,root,maxsize,now);
did[root]=true;
if(black[root]) td++;
for(int i=first[root];i!=-1;i=e[i].next){
if(!did[e[i].to]){
int max_deep=0;
if(black[e[i].to]) dfs_deep(e[i].to,root,1,max_deep);
else dfs_deep(e[i].to,root,0,max_deep);
v.push_back({e[i].to,e[i].val,max_deep});
}
}
sort(v.begin(),v.end(),cmp);
for(int i=0;i<v.size();i++){
if(black[v[i].to]) dfs_subtree(v[i].to,root,1,v[i].val);
else dfs_subtree(v[i].to,root,0,v[i].val);
int now=0;
if(i){
for(int j=v[i].deep;j>=0;j--){
while(now+j+td<k&&now<v[i-1].deep)
now++;
if(now+j+td<=k)
ans=max(ans,f[now]+tmp[j]);
}
}
for(int j=0;j<=v[i].deep;j++){
f[j]=max(f[j],tmp[j]);
tmp[j]=0;
}
for(int j=1;j<=v[i-1].deep;j++)
f[j]=max(f[j],f[j-1]);
}
if(v.size()){
for(int j=0;j<=v[v.size()-1].deep;j++){
if(j+td<=k) ans=max(ans,max(tmp[j],f[j]));
tmp[j]=f[j]=0;
}
}
for(int i=first[root];i!=-1;i=e[i].next)
if(!did[e[i].to])
solve(e[i].to);
}
int main(){
memset(first,-1,sizeof(first));
scanf("%d%d%d",&n,&k,&m);
for(int i=0,kk;i<m;i++){
scanf("%d",&kk);
black[kk]=true;
}
for(int i=0,u,v,w;i<n-1;i++){
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
}
solve(1);
printf("%d\n",ans);
}