題目測試的時候感覺題就很噁心,10道都不會,不過在正式比賽的時候還是有隊伍犀利的做出了6道,真的很厲害
這個是最水的一題了,但是對我還是很難,看著題解,劃拉半天才看明白。
代碼最佳化後JOJ跑了0.57s。(上午剛看明白,下午比賽就出了)
師兄給的題解:
樹狀DP
因為樹是連通的且為無向邊,所以可以假定1為根,從結點1開始向各個子樹DFS
這次DFS過程中要對每個結點記錄幾個量
node[i]記錄以i為根的子樹(包括i結點)的T[k] 和
sum[i]記錄以i為根的子樹(包括i結點)中,每個結點到根的路徑長度*T[k]的和
所以第1遍DFS得到的sum[1]是以1為the most convenient location的答案,那麼開始枚舉以不同結點為the most convenient location的情況
對於
1
/ \
2 3
/
4
比如轉移到2節點,則以2節點為the most convenient location 的答案是
Sum[1] – sum_node[2] * g[1][2] + ( sum_node[1] – sum_node[2]) * g[1][2] = Sum[2]
(g[1][2]表示1到2這條邊的權值)
接著對於以4節點為the most convenient location 的答案是
Sum[2] – sum_node[4] * g[2][4] + ( sum_node[1] – sum_node[4]) * g[2][4] = Sum[4]
以此類推,這樣子DFS一遍即可
#include <cstdio>#include <string.h>const int maxn=100005;struct Edge{ int v,next,w;}edge[2*maxn];int cnt,n,head[maxn];long long node[maxn],sum[maxn],sum_node[maxn];bool vis[maxn];void addedge(int u,int v,int w){ edge[cnt].v=v; edge[cnt].w=w; edge[cnt].next=head[u]; head[u]=cnt++; edge[cnt].v=u; edge[cnt].w=w; edge[cnt].next=head[v]; head[v]=cnt++;}void dfs (int u){ int v,i,t=head[u],w; vis[u]=true ; sum_node[u]=node[u]; for (;~t;t=edge[t].next) { v=edge[t].v; if(vis[v])continue; w=edge[t].w; dfs(v); sum_node[u]+=sum_node[v]; sum[0]+=w*sum_node[v]; }}void dfs2 (int u){ int v,t,w; vis[u]=true ; for (t=head[u]; ~t ; t=edge[t].next) { v=edge[t].v; if(vis[v])continue; w=edge[t].w; sum[v]=sum[u]+(sum_node[0]-(sum_node[v]<<1))*w; dfs2(v); }}inline void init (){ memset (head , -1 ,sizeof(head)); memset (vis , 0 , sizeof(vis)); sum[0]=0; cnt=0;}long long sovle (){ int i; dfs(0); memset (vis , false , sizeof(vis)); dfs2(0); long long min_num=sum[0]; for (i=0 ; i<n ; ++i) if(min_num>sum[i]) min_num=sum[i]; return min_num;}int main (){ int i,u,v,w; while (~scanf("%d",&n)) { init (); for (i=0 ; i<n ; ++i) scanf("%d",node+i); for (i=1 ; i<n ; ++i) { scanf("%d%d%d",&u,&v,&w); u--;v--; addedge (u,v,w); } printf("%lld\n",sovle()); } return 0;}