题意
求一棵树的最大点权和子树,且子树里任意两点的距离大于
题解
树上 (默认 为树根)
为了方便把将题目要求的大于 改成大于等于 ,即一开始
算法一
设 表示以 为根的子树里离 最近的点距离 为 的答案
那么对于 的子节点
可以发现这个算法复杂度是 ,而且答案也不好统计
算法二
设 表示以 为根的子树里离 最近的点与 的距离大于等于 的答案
显然 初值为
考虑将 的子节点 的答案合并到 的答案上去
实现时用一个临时数组将答案存下,枚举完后再更新 ,最后再求一遍后缀最大值
最后答案即为
时间复杂度为
算法三
考虑对算法二进行优化,设 表示 子树里的点离 最远的距离
那么上面 的枚举范围就可以缩小到 ,因为 大了之后 的值就不存在了
时间复杂度 长链长
算法四
为了方便将 往后整体挪一位,根据定义新的 ,那么
可以发现这样 与 就是对偶的了
然后可以注意到, 只会影响到 (因为长了就一定会有一个没有值了)
于是就可以考虑长链剖分,每次将短链合并到长链上
时间复杂度
void Merge(deque<int>&f,deque<int>&g){//启发式合并
if(f.size()<g.size())
swap(f,g);
vector<int>Tmp=vector<int>(g.begin(),g.end());//复制短链
for(int k=0;k<(int)g.size();++k){
int Res=max(k,K-k);
if(Res<(int)f.size())
Tmp[k]=max(Tmp[k],f[Res]+g[k]);
if(Res<(int)g.size())
Tmp[k]=max(Tmp[k],f[k]+g[Res]);
}
int Max=0;
for(int k=g.size()-1;k>=0;--k){
Max=max(Max,Tmp[k]);
f[k]=max(f[k],Max);
}//更新后缀最大值
}
deque<int> dsu(int u,int p){
deque<int>Now={a[u]};
for(auto v:G[u])if(v!=p){
deque<int>Son=dfs(v,u);
Son.push_front(Son.front());//整体向后移一位
Merge(Now,Son);
}
return Now;
}
算法五
考虑选了一个点 就加上他的权值 ,那么他周围与之距离小于 的点都不能选
考虑把这些点的权值都减去 ,相当于造成不选的影响
如果有的点点权变成了负数,那么这个点一定不会选
如果有的点点权还是正数,那就再加上这个点的权值进行 补差 ,就相当于选了这个点而没有选
然后就是选点顺序的问题,一种可行的方案就是从叶子节点开始,因为叶子节点没有儿子了,不会有后效性
时间复杂度
void dfs(int u,int p,int dis){
if(dis>=K)return;
a[u]-=Val;
for(auto v:G[u])if(v!=p)
dfs(v,u,dis+1);
}
inline void Bfs(){
int h=1,t=1;
q[1]=vis[1]=1;
while(h<=t){
int u=q[h++];
for(auto v:G[u])
if(!vis[v]){
vis[v]=1;
q[++t]=v;
}
}
}
inline int sol(){
int Ans=0;
Bfs();
for(int i=n;i>=1;--i){
int u=q[i];Val=a[u];
if(Val<=0)continue;
Ans+=Val;
dfs(u,0,0);
}
return Ans;
}