zoukankan      html  css  js  c++  java
  • 牛客多校第二场B discount 基环内向树

    题意:

    有n种商品,每种商品有一个价格 p[i] 。

    每种商品都有2种打折方式:

    1. 给你优惠 d[i] 元。

    2. 免费送你第 f[i] 种饮料。

    现在求每种饮料至少一瓶的最小花费。

    dp[i][0] 表示 i 的子树内所有的饮料都至少买了一瓶。

    dp[i][1] 表示 i 的子树内所有的饮料都至少买了一瓶 且 第i种饮料是使用第2种方式购买的。

    我们考虑树的转移方式。

    sum[i] 表示  dp[son[i]][0] 的和

    dp[i][1] = sum[i] + p[i]

    dp[i][0] = min(p[i]-d[i]+sum[i], sum[i] - dp[son[i][0] + dp[son[i][1] )

    然后我们可以先把环都抠出来, 然后将环上的边都标记一下,

    然后先把环的子树都转移到环上来, 最后再处理环的问题。

    假设一个环为 a -> b -> c ->  d  -> a ,mn 为这个环的最小花费。

    我们断开a -> b 这条边,并且 b 不是 通过 a 送来的。

    G[0][0] = dp[b][0] G[0][1] = dp[b][1]

    路上转移的状态为 G[1][1] = dp[c][1] + G[0][0];

    G[1][0] = min(G[0][1]+sum[c], dp[c][0] + G[0][0])

    这样一直转移到G[3][0] 

    因为我们规定b 不是 通过 a 送来的。 mn = min(mn, G[3][0])

    然后我们假设 b 是通过 a 送过来的

    G[0][0] = sum[b]    G[0][1] = dp[b][1]

    然后通过上面的转移方程转移到G[3][1]。

    因为b是a送的 那么 a 必须要按方式1购买 所以 mn = min(mn, G[3][1])

    最后将 mn 加入答案中。

    然后 跑完所有环之后 就能得到答案了。

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout);
      4 #define LL long long
      5 #define ULL unsigned LL
      6 #define fi first
      7 #define se second
      8 #define pb push_back
      9 #define lson l,m,rt<<1
     10 #define rson m+1,r,rt<<1|1
     11 #define max3(a,b,c) max(a,max(b,c))
     12 #define min3(a,b,c) min(a,min(b,c))
     13 typedef pair<int,int> pll;
     14 const int inf = 0x3f3f3f3f;
     15 const LL INF = 0x3f3f3f3f3f3f3f3f;
     16 const LL mod =  (int)1e9+7;
     17 const int N = 1e5 + 100;
     18 int p[N], d[N], f[N];
     19 int head[N], to[N], nt[N];
     20 int vis[N];
     21 int cntCir = 0, tot = 0, top, n;
     22 int sta[N];
     23 vector<int> cir[N];
     24 int vvis[N];
     25 LL dp[N][2];
     26 LL sum[N];
     27 LL G[N][2];
     28 void add(int u, int v){
     29     to[tot] = v;
     30     nt[tot] = head[u];
     31     head[u] = tot++;
     32 }
     33 void getCir(int u){
     34     if(vis[u] == 1) return ;
     35     if(vis[u] == -1){
     36         cntCir++;
     37         for(int i = top; i >= 1; i--){
     38             cir[cntCir].pb(sta[i]);
     39             vvis[sta[i]] = 1;
     40             if(sta[i] == u) break;
     41         }
     42         return;
     43     }
     44     vis[u] = -1;
     45     sta[++top] = u;
     46     getCir(f[u]);
     47     top--;
     48     vis[u] = 1;
     49 }
     50 void dfs(int u){
     51     for(int i = head[u]; ~i; i = nt[i]){
     52         if(vvis[to[i]]) continue;
     53         dfs(to[i]);
     54         sum[u] += dp[to[i]][0];
     55     }
     56     dp[u][1] = sum[u] + p[u];
     57     dp[u][0] = sum[u] + p[u] - d[u];
     58     for(int i = head[u]; ~i; i = nt[i]){
     59         if(vvis[to[i]]) continue;
     60         dp[u][0] = min(dp[u][0], sum[u] - dp[to[i]][0] + dp[to[i]][1]);
     61     }
     62 }
     63 int main(){
     64     memset(head, -1, sizeof(head));
     65     scanf("%d", &n);
     66     for(int i = 1; i <= n; i++) scanf("%d", &p[i]);
     67     for(int i = 1; i <= n; i++) scanf("%d", &d[i]);
     68     for(int i = 1; i <= n; i++) {
     69         scanf("%d", &f[i]);
     70         add(f[i], i);
     71     }
     72     for(int i = 1; i <= n; i++){
     73         if(!vis[i]){
     74             top = 0;
     75             getCir(i);
     76         }
     77     }
     78     LL ans = 0;
     79     for(int i = 1; i <= cntCir; i++){
     80         reverse(cir[i].begin(), cir[i].end());
     81         for(int j = 0; j < cir[i].size(); j++){
     82             dfs(cir[i][j]);
     83         }
     84         LL mn = INF;
     85         G[0][0] = dp[cir[i][0]][0];
     86         G[0][1] = dp[cir[i][0]][1];
     87         for(int j = 1; j < cir[i].size(); j++){
     88             G[j][1] = G[j-1][0] + dp[cir[i][j]][1];
     89             G[j][0] = min(G[j-1][1]+sum[cir[i][j]], dp[cir[i][j]][0]+G[j-1][0]);
     90         }
     91         mn = min(mn, G[cir[i].size()-1][0]);
     92         G[0][0] = sum[cir[i][0]];
     93         G[0][1] = dp[cir[i][0]][1];
     94         for(int j = 1; j < cir[i].size(); j++){
     95             G[j][1] = G[j-1][0] + dp[cir[i][j]][1];
     96             G[j][0] = min(G[j-1][1]+sum[cir[i][j]], dp[cir[i][j]][0]+G[j-1][0]);
     97         }
     98         mn = min(mn, G[cir[i].size()-1][1]);
     99         ans += mn;
    100     }
    101     printf("%lld
    ", ans);
    102     return 0;
    103 }
    View Code
  • 相关阅读:
    C语言 数组排序 – 冒泡法排序
    类加载过程
    布隆过滤器的原理及应用
    回答两个被频繁问到的代码写法问题
    关于分布式事务的理解
    根据使用者反馈,对开源项目 go-gin-api 新增两个功能
    图书速度 | 《如何高效学习》
    报错kernel:NMI watchdog: BUG: soft lockup
    容器Jenkins中运行docker
    容器Jenkins中运行docker
  • 原文地址:https://www.cnblogs.com/MingSD/p/9370197.html
Copyright © 2011-2022 走看看