牛客多校第二场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\n", ans);
102     return 0;
103 }
View Code

 

posted @ 2018-07-26 10:30  Schenker  阅读(309)  评论(0编辑  收藏  举报