【bzoj1827】[Usaco2010 Mar]gather 奶牛大集会 树形dp+贪心

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

由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;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值