題目鏈接:點擊查看
題目大意:給一個具有 N 個節點的有根樹,以 1 號節點為根,節點編號從 1 開始,點有點權。樹的第 H 層權值為深度為 H 的所有點的點權之和。樹的總權值為所有層權值的最大值。問分別割掉以 1,2,..,N 為根的子樹后,剩余樹的總權值為多少。
題目分析:因為題目提示了,弱化數據后支持雙 log 的算法。。所以不難想到樹上啟發式合并+線段樹,兩個模板套起來就過了,線段樹負責處理區間最大值問題,每次更新子樹信息的時候,單點更新一下相關層的最大值就好了,記得回溯
代碼:
?
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
using namespace std;typedef long long LL;typedef unsigned long long ull;const LL inf=0x3f3f3f3f;const int N=1e5+100;LL a[N],sum[N],ans[N];struct Node
{int l,r;LL mmax;
}tree[N<<2];void pushup(int k)
{tree[k].mmax=max(tree[k<<1].mmax,tree[k<<1|1].mmax);
}void build(int k,int l,int r)
{tree[k].l=l;tree[k].r=r;if(l==r){tree[k].mmax=sum[l];return;}int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);pushup(k);
}void update(int k,int pos,int val)
{if(tree[k].l==tree[k].r){tree[k].mmax+=val;return;}int mid=tree[k].l+tree[k].r>>1;if(pos<=mid)update(k<<1,pos,val);elseupdate(k<<1|1,pos,val);pushup(k);
}int num[N],son[N],deep[N],max_deep;bool vis[N];vector<int>node[N];void dfs_son(int u,int fa,int dep)//樹鏈剖分跑出重鏈
{max_deep=max(max_deep,dep);sum[dep]+=a[u];deep[u]=dep;son[u]=-1;num[u]=1;for(auto v:node[u]){if(v==fa)continue;dfs_son(v,u,dep+1);num[u]+=num[v];if(son[u]==-1||num[v]>num[son[u]])son[u]=v;}
}void cal(int u,int fa,int val)//對于每個節點計算其子樹的貢獻
{update(1,deep[u],val*a[u]);for(auto v:node[u]){if(v==fa||vis[v])continue;cal(v,u,val);}
}void dfs(int u,int fa,int keep)//啟發式合并
{for(auto v:node[u]){if(v==fa||v==son[u])continue;dfs(v,u,0);}if(son[u]!=-1){dfs(son[u],u,1);vis[son[u]]=true;}cal(u,fa,-1);ans[u]=tree[1].mmax;if(son[u]!=-1)vis[son[u]]=false;if(!keep)cal(u,fa,1);
}int main()
{
#ifndef ONLINE_JUDGE
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);int n;scanf("%d",&n);for(int i=1;i<n;i++){int u,v;scanf("%d%d",&u,&v);node[u].push_back(v);node[v].push_back(u);}for(int i=1;i<=n;i++)scanf("%lld",a+i);dfs_son(1,-1,1);build(1,1,max_deep);dfs(1,-1,1);printf("%lld",ans[1]);for(int i=2;i<=n;i++)printf(" %lld",ans[i]);puts("");return 0;
}
?
總結
以上是生活随笔為你收集整理的山东理工大学第十二届ACM程序设计竞赛 - Cut the tree(树上启发式合并+线段树)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。