题解 P6513 【[QkOI#R1] Quark and Tree】
首先不考虑加边,若仅计算以某个点为根时树中所有点的深度与点权乘积之和,可以用换根 DP 处理。
指定
设以
下面给出一棵树为例:
为保证
则所有点在基环树中的深度变为它到环上的点的最短距离,可以发现离一个点最近的环上的点必然是其本身或其祖先节点中在环上的深度最深的点。这里设离点
考虑将
所以最终的问题就是找出两条与
最终只需要枚举点
时间复杂度
代码:
//似乎挺多人反映细节多qwq,但出题人感觉这么想细节并不会太多?
#include <cstdio>
#include <cctype>
#include <cstring>
#define MaxN (1000005)
using namespace std;
typedef long long ll;
void Read(int &x){
static char c;
static int f;
f=1;
while(!isdigit(c=getchar()))
if(c=='-')
f=-1;
x=(c^48);
while(isdigit(c=getchar()))
x=x*10+(c^48);
x*=f;
return;
}
void Print(ll x){
if(x<0)
putchar('-'),x=-x;
static ll i;
for(i=1;i*10LL<=x;i*=10);
for(;i;i/=10)
putchar(x/i%10|48);
putchar('\n');
return;
}
inline ll mmax(ll a,ll b){return a>b?a:b;}
inline ll mmin(ll a,ll b){return a<b?a:b;}
struct LnkNode{
int v,nxt;
}edge[MaxN*2];
int etot,fst[MaxN];
inline void addedge(int u,int v){
++etot;
edge[etot].v=v;
edge[etot].nxt=fst[u];
fst[u]=etot;
return;
}
int n,w[MaxN],s[MaxN];
ll d[MaxN],dm[MaxN],sdm[MaxN],res=-9223372036854775807LL;
void dfs1(int x,int f){
s[x]=w[x];
ll t;
for(int i=fst[x];i;i=edge[i].nxt)
if(edge[i].v!=f){
dfs1(edge[i].v,x);
s[x]+=s[edge[i].v];
d[x]+=d[edge[i].v]+s[edge[i].v];
t=s[edge[i].v]+mmin(dm[edge[i].v],0);
if(t<dm[x])
sdm[x]=dm[x],dm[x]=t;
else if(t<sdm[x])
sdm[x]=t;
}
return;
}
void dfs2(int x,int f){
if(f){
d[x]=d[f]+s[1]-s[x]*2;
ll t=s[1]-s[x]+mmin((dm[f]!=s[x]+mmin(dm[x],0))?(dm[f]):(sdm[f]),0);
if(t<dm[x])
sdm[x]=dm[x],dm[x]=t;
else if(t<sdm[x])
sdm[x]=t;
}
if(sdm[x]!=0x3f3f3f3f3f3f3f3fLL && d[x]-dm[x]-sdm[x]>res)
res=d[x]-dm[x]-sdm[x];
for(int i=fst[x];i;i=edge[i].nxt)
if(edge[i].v!=f)
dfs2(edge[i].v,x);
return;
}
int main(){
int a,b;
Read(n);
memset(dm+1,0x3f,sizeof(ll)*n);
memset(sdm+1,0x3f,sizeof(ll)*n);
for(int i=1;i<=n;++i)
Read(w[i]);
for(int i=1;i<n;++i){
Read(a);
Read(b);
addedge(a,b);
addedge(b,a);
}
dfs1(1,0);
dfs2(1,0);
Print(res);
return 0;
}