Live2d Test Env

2018牛客网暑期ACM多校训练营(第二场):discount(基环树DP)

题意:有N个不同的商品,每个商品原价是Pi元,如果选择打折,可以减少Di元。  现在加一种规则,每个商品有一个友好商品Fai,如果i用原价买,则可以免费买Fai。

现在问买到所有物品的最小价格。

思路:显然是一个内向树基环。 先把悬在环上的树都求出DP[][],然后再在链上同理跑一遍DP。

我们先看树:dp[i][0]表示i节点不是原价买,dp[i][1]是原价买。 dp[i][1]对儿子没有任何要求,而dp[i][0]=min(子树随便买+自己打折买, 子树至少一个原价买+自己免费买);

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=200010;
const ll inf=1LL<<60;
int fa[maxn],Laxt[maxn],Next[maxn],To[maxn],vis[maxn];
int p[maxn],d[maxn],cnt=1,N,r[maxn],tot;
ll dp[maxn][2],sum[maxn],C[maxn][2],ans;//0是打折或者免费,1是原价
void add(int u,int v)
{
    Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v;
}
bool dfs(int u)
{
    while(!vis[u]) vis[u]=1,u=fa[u];
    while(vis[u]==1) vis[u]=2,r[++tot]=u,u=fa[u];
}
void treedp(int u,int f)
{
    if(!vis[u]) vis[u]=1; //所以不要忘了标记,免得重复。
    ll sum1=0,Mn=inf;
    for(int i=Laxt[u];i;i=Next[i]){
        if(To[i]==f||To[i]==u||vis[To[i]]==2) continue;
        int v=To[i]; treedp(v,u);
        sum1+=dp[v][0];
        Mn=min(Mn,dp[v][1]-dp[v][0]);
    }
    sum[u]=sum1;
    dp[u][1]=sum1+p[u];
    dp[u][0]=min(sum1+Mn,sum1+p[u]-d[u]);
}
ll solve(int u)
{
    tot=0;  dfs(u); //找环
    rep(i,1,tot) treedp(r[i],0);
    ll res=inf;
    rep(x,0,1){ //1表示尾巴原价买
        if(x==0) C[1][0]=dp[r[1]][0];
        else C[1][0]=sum[r[1]];
        C[1][1]=dp[r[1]][1];
        rep(i,2,tot) {
            C[i][0]=min(C[i-1][0]+dp[r[i]][0],C[i-1][1]+sum[r[i]]);
            C[i][1]=dp[r[i]][1]+C[i-1][0];
        }
        res=min(res,C[tot][x]);
    }
    return res;
}
int main()
{
    scanf("%d",&N);
    rep(i,1,N) scanf("%d",&p[i]);
    rep(i,1,N) scanf("%d",&d[i]);
    rep(i,1,N){
         scanf("%d",&fa[i]);
         add(fa[i],i);
    }
    rep(i,1,N) if(!vis[i]) ans+=solve(i);
    printf("%lld\n",ans);
    return 0;
}

 

posted @ 2019-09-06 16:34  nimphy  阅读(316)  评论(0编辑  收藏  举报