题意:
有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 }