由x转移到y的答案为ans=ans+val∗(s[1]−s[y]∗2)
可以看出只有s[1]−s[y]∗2<0答案才会减小,所以贪心即可
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int N=1e5+10;
ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
ll ans,C[N],dis[N],s[N];
int n;
vector<int>G[N],v[N];
ll dfs(int x,int fa)
{
s[x]=C[x];
ll tot=dis[x]*C[x];
for(int i=0;i<G[x].size();i++)
{
int id=G[x][i],w=v[x][i];
if(id==fa)continue;
dis[id]=dis[x]+w;
tot+=dfs(id,x);
dfs(id,x);
s[x]+=s[id];
}
return tot;
}
void solve(int x,int fa)
{
for(int i=0;i<G[x].size();i++)
{
int id=G[x][i],w=v[x][i];
if(id==fa)continue;
if((s[id]<<1)>s[1]){ans=ans+(w*(s[1]-(s[id]<<1)));solve(id,x);}
}
}
int main()
{
n=read();
fo(i,1,n)C[i]=read();
for(int from,to,val,i=1;i<n;i++)
{
from=read();to=read();val=read();
G[from].push_back(to);G[to].push_back(from);
v[from].push_back(val);v[to].push_back(val);
}
ans=dfs(1,0);
solve(1,0);
printf("%lld\n",ans);
return 0;
}
//PS:莫名T了
附上一开始没思路,打的O(n2logn)的树上倍增暴力
T_T比裸暴力还多一个log(┬_┬)
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e5+10,inf=(1<<30);
int f[N][25],dis[N],last[N],dep[N],len=0;
int n,C[N];
struct Edge{int to,next,val;Edge(int to=0,int next=0,int val=0):to(to),next(next),val(val){}}e[N<<1];
void add_edge(int u,int v,int w){e[++len]=Edge(v,last[u],w);last[u]=len;}
void dfs(int x,int fa)
{
for(int i=1;i<=20;i++)
{
if(dep[x]<(1<<i))break;
f[x][i]=f[f[x][i-1]][i-1];
}
for(int i=last[x];i;i=e[i].next)
{
if(e[i].to==fa)continue;
f[e[i].to][0]=x;
dep[e[i].to]=dep[x]+1;
dis[e[i].to]=dis[x]+e[i].val;
dfs(e[i].to,x);
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
int d=dep[x]-dep[y];
for(int i=0;i<=20;i++)
if(d&(1<<i))x=f[x][i];
for(int i=20;i+1;i--)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
if(x==y)return x;
else return f[x][0];
}
int main()
{
scanf("%d",&n);
fo(i,1,n)scanf("%d",&C[i]);
for(int u,v,w,i=1;i<n;i++)
{
scanf("%d%d%d",&u,&v,&w);
add_edge(u,v,w);add_edge(v,u,w);
}
dfs(1,0);
int ans=inf;
for(int i=1;i<=n;i++)
{
int tmp=0;
for(int j=1;j<=n;j++)
tmp+=(dis[i]+dis[j]-2*dis[lca(i,j)])*C[j];
ans=min(ans,tmp);
}
printf("%d\n",ans);
return 0;
}

博客探讨了Usaco2010 March竞赛中的一道题目——gather,重点在于如何利用树形DP和贪心策略解决问题。文章指出,从x转移到y时,答案的变化取决于s[1]与s[y]的特定关系,并展示了当s[1] - s[y] * 2小于0时,答案会减少的情况。博主分享了一开始尝试的O(n^2 log n)树上倍增暴力解法,虽然效率不高,但有助于理解问题的本质。
1725

被折叠的 条评论
为什么被折叠?



