zoukankan      html  css  js  c++  java
  • 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
    ",ans);
        return 0;
    }
  • 相关阅读:
    正斜杠/和反斜杠的区别
    Ghost文件封装说明
    装机自动化脚本介绍
    ubuntu 11.04侧边栏怎么添加图标
    samba的安装和配置
    vim使用大全
    ubuntu中运行python脚本
    ubuntu中使用usb-creator制作live usb
    ubuntu中安装ftp服务器
    ubuntu命令查询版本和内核版本
  • 原文地址:https://www.cnblogs.com/hua-dong/p/11475625.html
Copyright © 2011-2022 走看看