牛客挑战赛52天平

牛客挑战赛52天平

题意:

给一个二叉树有 n n n个节点,这个二叉树中的每个节点有么有2个儿子要么没有儿子。给定 m m m个数,这 m m m个数有任意多个,你可以把每个节点赋值为这 m m m个数中的一个(可以重复使用),整体赋值完后,每个非叶子节点的贡献为 l v lv lv+ r v rv rv,并且满足 s [ l s [ u ] ] × l v = s [ r s [ u ] ] × r v s[ls[u]]\times lv=s[rs[u]]\times rv s[ls[u]]×lv=s[rs[u]]×rv,( s [ l s [ u ] ] , s [ r s [ u ] ] s[ls[u]],s[rs[u]] s[ls[u]],s[rs[u]]为赋值完后的左子树和右子树的点权和),题目要求最小化所有非叶子节点的贡献和。

1 ≤ v ≤ 50 1\le v \le 50 1v50 1 ≤ n ≤ 50 1\le n \le 50 1n50

Sol

要使 l v , r v lv,rv lv,rv最小,显然 l v = s [ l s [ u ] ] / g c d ( s [ l s [ u ] ] , s [ r s [ u ] ] ) , r v = s [ r s [ u ] ] / g c d ( s [ l s [ u ] ] , s [ r s [ u ] ] ) lv=s[ls[u]]/gcd(s[ls[u]],s[rs[u]]),rv=s[rs[u]]/gcd(s[ls[u]],s[rs[u]]) lv=s[ls[u]]/gcd(s[ls[u]],s[rs[u]]),rv=s[rs[u]]/gcd(s[ls[u]],s[rs[u]])

v a l [ i ] [ j ] = i / ( i , j ) + j / ( i , j ) val[i][j]=i/(i,j)+j/(i,j) val[i][j]=i/(i,j)+j/(i,j)

d p [ i ] [ j ] dp[i][j] dp[i][j]表示以 i i i为节点,并且 i i i的子树和包括自身点权为 j j j的最小贡献和

则枚举 m m m个数,记为 a a a,并枚举每一个左右子树和 j j j,则转移为 d p [ i ] [ j + a ] = m i n ( d p [ i ] [ j + a ] , d p [ l s [ u ] ] [ j 1 ] + d p [ r s [ u ] ] [ j 2 ] + v a l [ j 1 ] [ j 2 ] ) dp[i][j+a]=min(dp[i][j+a],dp[ls[u]][j_1]+dp[rs[u]][j_2]+val[j_1][j_2]) dp[i][j+a]=min(dp[i][j+a],dp[ls[u]][j1]+dp[rs[u]][j2]+val[j1][j2]),其中 ( j 1 + j 2 = = j ) (j_1+j_2==j) (j1+j2==j)

(每一个点的子树和上限为 50 ∗ 50 50*50 5050,可以每次转移前处理出 d p [ l s [ u ] ] [ j 1 ] + d p [ r s [ u ] ] [ j 2 ] + v a l [ j 1 ] [ j 2 ] dp[ls[u]][j_1]+dp[rs[u]][j_2]+val[j_1][j_2] dp[ls[u]][j1]+dp[rs[u]][j2]+val[j1][j2],则转移为 O ( m ∗ 2500 ) O(m*2500) O(m2500))

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=55;
const ll inf=1e18;
int ls[N],rs[N];
ll dp[N][N*N],sz[N];
int a[N];
ll v[N*N][N*N],tmp[N*N];
int n,m;
int gcd(int x,int y)
{
    return y==0?x:gcd(y,x%y);
}
void dfs(int u)
{   
    sz[u]=1;
    if(!ls[u])
    {
        for(int i=1;i<=50;i++) dp[u][i]=inf;
        for(int i=1;i<=m;i++) dp[u][a[i]]=0;
        return;
    }
    dfs(ls[u]),dfs(rs[u]);
    sz[u]=sz[u]+sz[ls[u]]+sz[rs[u]];
    for(int i=1;i<=sz[u]*50;i++) tmp[i]=inf;

    for(int i=1;i<=sz[ls[u]]*50;i++)
    for(int j=1;j<=sz[rs[u]]*50;j++)
    {
        tmp[i+j]=min(tmp[i+j],dp[ls[u]][i]+dp[rs[u]][j]+v[i][j]);
    }
    for(int i=1;i<=sz[u]*50;i++)
    {
        dp[u][i]=inf;
        for(int j=1;j<=m;j++) 
        if(i>a[j])dp[u][i]=min(dp[u][i],tmp[i-a[j]]);
    }
}
int main()
{
    ios::sync_with_stdio(false);

    cin>>n>>m;
    for(int i=1;i<=m;i++) cin>>a[i];
    
    int u,x,y;
    for(int i=1;i<=(n-1)/2;i++)
    {
		cin>>u>>x>>y;
        ls[u]=x;
        rs[u]=y;
    }
    
    for(int i=1;i<=2500;i++)
    for(int j=1;j<=2500;j++)
    {
        int p=gcd(i,j);
        v[i][j]=i/p+j/p;
        
    }

    dfs(1);

    ll ans=inf;
    for(int i=1;i<=n*50;i++) ans=min(ans,dp[1][i]);
    cout<<ans<<endl;
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值