Description
有nn种饮料,第种饮料价格为p[i]p[i],当买第ii种饮料时有两种优惠:
1.直接优惠元,也就是说只需要花费p[i]−d[i]p[i]−d[i]买第ii种饮料
2.免费得到第种饮料
问这nn种饮料每种至少有一个所需的最少花费
Input
第一行一整数表示饮料种类,之后输入nn个整数表示每种饮料的价格,之后输入nn个整数,最后输入nn个整数
(1≤n≤105,0≤d[i]≤p[i]≤109,1≤f[i]≤n)(1≤n≤105,0≤d[i]≤p[i]≤109,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]表示以uu为根的子树中每点都至少买一次的最少代价,以表示以uu为根的子树中每点都至少买一次,且以第二种优惠方式购买点的最少代价(即可以赠送uu的父亲节点),那么有转移
g[u][0]=min(p[u]−d[u]+∑v∈son(u)g[v][0],minw∈son(u)g[w][1]+∑v∈son(u),v≠wg[v][0])g[u][0]=min(p[u]−d[u]+∑v∈son(u)g[v][0],minw∈son(u)g[w][1]+∑v∈son(u),v≠wg[v][0])
其中g[u][0]g[u][0]的转移即为要么用第一种优惠购买uu,要么是通过购买uu的某个儿子赠送的
令sum[u]=∑v∈son(u)g[v][0]sum[u]=∑v∈son(u)g[v][0],则上述转移可以写成
g[u][1]=p[u]+sum[u]g[u][1]=p[u]+sum[u]
g[u][0]=min(p[u]−d[u]+sum[u],minw∈son(u)(g[w][1]−g[w][0])+sum[u])g[u][0]=min(p[u]−d[u]+sum[u],minw∈son(u)(g[w][1]−g[w][0])+sum[u])
下面考虑基环的情况,假设环上的点为v1,...,vmv1,...,vm,枚举v1v1的转移,即v1v1是否要通过购买vmvm赠送得到,以sta=0/1sta=0/1表示v1v1不用/用通过购买vmvm赠送得到,以dp[i][0]dp[i][0]表示购买v1,...,viv1,...,vi以及其外挂着的树上所有节点至少一次所需最少代价,dp[i][1]dp[i][1]表示购买v1,...,viv1,...,vi以及其外挂着的树上所有节点至少一次且以第二种优惠方式购买vivi的最少代价,那么
dp[1][1]=g[v1][1],dp[1][0]=sta?sum[v1]:dp[v1][0]dp[1][1]=g[v1][1],dp[1][0]=sta?sum[v1]:dp[v1][0],并有转移
dp[i][1]=dp[i−1][0]+g[vi][1]dp[i][1]=dp[i−1][0]+g[vi][1]
dp[i][0]=min(dp[i−1][1]+sum[vi],dp[i−1][0]+g[vi][0])dp[i][0]=min(dp[i−1][1]+sum[vi],dp[i−1][0]+g[vi][0])
答案即为dp[m][sta]dp[m][sta],枚举stasta选取较优值即为答案
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;
}