BZOJ3302: [Shoi2005]樹的雙中心
阿新 • • 發佈:2019-01-01
BZOJ3302: [Shoi2005]樹的雙中心
https://lydsy.com/JudgeOnline/problem.php?id=3302
分析:
- 樸素演算法 : 列舉邊,然後在兩個連通塊內部找到帶權重心計算答案。
- 然後我們發現在內部找重心是方向唯一,因此可以預處理出來這個點向下走的最優兒子和次優兒子。
- 然後每次從上往下找重心即可。
- 時間複雜度\(O(n\times dep)\)。
程式碼:
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> using namespace std; typedef long long ll; #define N 50050 #define ls ch[x][0] #define rs ch[x][1] int head[N],to[N<<1],nxt[N<<1],cnt,n,ch[N][2],dep[N]; ll w[N],sw[N],sd[N],ans; inline void add(int u,int v) { to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; } void d1(int x,int y) { int i; sw[x]=w[x]; for(i=head[x];i;i=nxt[i]) if(to[i]!=y) { dep[to[i]]=dep[x]+1; d1(to[i],x); sw[x]+=sw[to[i]]; sd[x]+=sd[to[i]]+sw[to[i]]; if(sd[to[i]]>sd[ls]) rs=ls,ls=to[i]; else if(sd[to[i]]>sd[rs]) rs=to[i]; } } ll now,tot,sz; int ins[N]; ll calc(int x,int y) { ll sy=sw[y]-(ins[y])*sz; return now-sy+tot-sy; } int rt; void d3(int x) { ll t1=calc(x,ls),t2=calc(x,rs); if(t1<t2) {if(t1<now)now=t1,d3(ch[x][0]);} else if(t2<now) now=t2,d3(ch[x][1]); } void d2(int x,int y) { int i; ins[x]=1; for(i=head[x];i;i=nxt[i]) if(to[i]!=y) { ins[to[i]]=1; ll tmp=0; rt=x; tot=sw[to[i]]; sz=0; now=sd[to[i]]; d3(to[i]); tmp+=now; tot=sw[1]-sw[to[i]]; sz=sw[to[i]]; now=sd[1]-sd[to[i]]-dep[to[i]]*sw[to[i]]; d3(1); tmp+=now; ans=min(ans,tmp); d2(to[i],x); } ins[x]=0; } int main() { scanf("%d",&n); int i,x,y; for(i=1;i<n;i++) { scanf("%d%d",&x,&y); add(x,y); add(y,x); } for(i=1;i<=n;i++) scanf("%lld",&w[i]); sd[0]=0; d1(1,0); ans=sd[1]; d2(1,0); printf("%lld\n",ans); }