Newcoder 140 B.discount(树形DP)

本文探讨了在多种饮料选择中,利用直接折扣和免费赠品的优惠策略,通过构建数学模型和算法,寻找购买n种饮料所需的最少花费。文章详细介绍了如何通过树状结构和动态规划求解最优解,适用于大规模数据处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

n n 种饮料,第i种饮料价格为 p[i] p [ i ] ,当买第 i i 种饮料时有两种优惠:

1.直接优惠d[i]元,也就是说只需要花费 p[i]d[i] p [ i ] − d [ i ] 买第 i i 种饮料

2.免费得到第f[i]种饮料

问这 n n 种饮料每种至少有一个所需的最少花费

Input

第一行一整数n表示饮料种类,之后输入 n n 个整数p[1],...,p[n]表示每种饮料的价格,之后输入 n n 个整数d[1],...,d[n],最后输入 n n 个整数f[1],...,f[n]

(1n105,0d[i]p[i]109,1f[i]n) ( 1 ≤ n ≤ 10 5 , 0 ≤ d [ i ] ≤ p [ i ] ≤ 10 9 , 1 ≤ f [ i ] ≤ n )

Output

输出最小花费

Sample Input

3
10 3 5
5 0 5
1 3 2

Sample Output

8

Solution

以赠送关系反向建图构成若干基环,首先考虑树上问题,则第二种优惠方式为购买某个儿子节点可以赠送该点,以 g[u][0] g [ u ] [ 0 ] 表示以 u u 为根的子树中每点都至少买一次的最少代价,以g[u][1]表示以 u u 为根的子树中每点都至少买一次,且以第二种优惠方式购买u点的最少代价(即可以赠送 u u 的父亲节点),那么有转移

g[u][1]=p[u]+vson(u)g[v][0]

g[u][0]=min(p[u]d[u]+vson(u)g[v][0],minwson(u)g[w][1]+vson(u),vwg[v][0]) g [ u ] [ 0 ] = min ( p [ u ] − d [ u ] + ∑ v ∈ s o n ( u ) g [ v ] [ 0 ] , min w ∈ s o n ( u ) g [ w ] [ 1 ] + ∑ v ∈ s o n ( u ) , v ≠ w g [ v ] [ 0 ] )

其中 g[u][0] g [ u ] [ 0 ] 的转移即为要么用第一种优惠购买 u u ,要么u是通过购买 u u 的某个儿子w赠送的

sum[u]=vson(u)g[v][0] s u m [ u ] = ∑ v ∈ s o n ( u ) g [ v ] [ 0 ] ,则上述转移可以写成

g[u][1]=p[u]+sum[u] g [ u ] [ 1 ] = p [ u ] + s u m [ u ]

g[u][0]=min(p[u]d[u]+sum[u],minwson(u)(g[w][1]g[w][0])+sum[u]) g [ u ] [ 0 ] = min ( p [ u ] − d [ u ] + s u m [ u ] , min w ∈ s o n ( u ) ( g [ w ] [ 1 ] − g [ w ] [ 0 ] ) + s u m [ u ] )

下面考虑基环的情况,假设环上的点为 v1,...,vm v 1 , . . . , v m ,枚举 v1 v 1 的转移,即 v1 v 1 是否要通过购买 vm v m 赠送得到,以 sta=0/1 s t a = 0 / 1 表示 v1 v 1 不用/用通过购买 vm v m 赠送得到,以 dp[i][0] d p [ i ] [ 0 ] 表示购买 v1,...,vi v 1 , . . . , v i 以及其外挂着的树上所有节点至少一次所需最少代价, dp[i][1] d p [ i ] [ 1 ] 表示购买 v1,...,vi v 1 , . . . , v i 以及其外挂着的树上所有节点至少一次且以第二种优惠方式购买 vi v i 的最少代价,那么

dp[1][1]=g[v1][1],dp[1][0]=sta?sum[v1]:dp[v1][0] d p [ 1 ] [ 1 ] = g [ v 1 ] [ 1 ] , d p [ 1 ] [ 0 ] = s t a ? s u m [ v 1 ] : d p [ v 1 ] [ 0 ] ,并有转移

dp[i][1]=dp[i1][0]+g[vi][1] d p [ i ] [ 1 ] = d p [ i − 1 ] [ 0 ] + g [ v i ] [ 1 ]

dp[i][0]=min(dp[i1][1]+sum[vi],dp[i1][0]+g[vi][0]) d p [ i ] [ 0 ] = min ( d p [ i − 1 ] [ 1 ] + s u m [ v i ] , d p [ i − 1 ] [ 0 ] + g [ v i ] [ 0 ] )

答案即为 dp[m][sta] d p [ m ] [ s t a ] ,枚举 sta s t a 选取较优值即为答案

Code

#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
#define maxn 100005
int n,p[maxn],d[maxn],f[maxn],vis[maxn],mark[maxn];
ll g[maxn][2],dp[maxn][2],sum[maxn];
vector<int>e[maxn];
void dfs(int u)
{
    vis[u]=1;
    ll mn=1e16;
    for(int i=0;i<e[u].size();i++)
    {
        int v=e[u][i];
        if(mark[v])continue;
        dfs(v);
        sum[u]+=g[v][0];
        mn=min(mn,g[v][1]-g[v][0]);
    }
    g[u][1]=p[u]+sum[u];
    g[u][0]=min(p[u]-d[u]+sum[u],mn+sum[u]);
}
ll Solve(int u)
{
    while(!vis[u])vis[u]=1,u=f[u];
    vector<int>v;
    v.clear();
    v.push_back(u);
    while(f[v.back()]!=u)v.push_back(f[v.back()]);
    for(int i=0;i<v.size();i++)mark[v[i]]=1;
    for(int i=0;i<v.size();i++)dfs(v[i]);
    ll ans=1e16;
    for(int sta=0;sta<=1;sta++)
    {
        dp[0][1]=g[v[0]][1];
        dp[0][0]=sta?sum[v[0]]:g[v[0]][0];
        for(int i=1;i<v.size();i++)
        {
            dp[i][1]=g[v[i]][1]+dp[i-1][0];
            dp[i][0]=min(dp[i-1][1]+sum[v[i]],dp[i-1][0]+g[v[i]][0]);
        }
        ans=min(ans,dp[v.size()-1][sta]);
    }
    return ans;
}
int main()
{   
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&p[i]);
    for(int i=1;i<=n;i++)scanf("%d",&d[i]);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&f[i]);
        e[f[i]].push_back(i);
    }
    ll ans=0;
    for(int i=1;i<=n;i++)
        if(!vis[i])ans+=Solve(i);
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值