B - discount
题目大意:有n种饮料,每种饮料的价格为p[ i ]。
购买第 i 种饮料的时候,你可以在一下两种优惠中选择一种:
1:该饮料优惠d[ i ]元
2:免费送一瓶第f[ i ]种饮料
问最少花费多少钱使得每种饮料至少都有一瓶。
思路:最后组成一个基环内向图,先考虑树上的情况,我们设
dp[ i ][ 0 ]表示以 i 为子树的全部饮料至少有一瓶的最少花费
dp[ i ][ 1 ]表示以 i 为子树的全部饮料至少有一瓶的最少花费,并且第 i 中饮料用了第二种优惠
这样树上的就很容易dp出来。(dp的时候把环上的边都断开)
然后对环进行dp,我们随便选一条边断开,让它成为一条线段,然后在线段上进行dp,因为原来是环,
我们需要枚举初始位置的状态,即第一个点受不受最后一个点的影响。
#include<bits/stdc++.h> #define LL long long #define fi first #define se second #define mk make_pair #define pii pair<int, int> #define y1 skldjfskldjg #define y2 skldfjsklejg using namespace std; const int N = 1e5 + 7; const int M = 1e5 + 7; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const int mod = 1e9 +7; int n, tot, top, m, p[N], d[N], f[N], head[N], stk[N]; int vis[N]; bool is[N]; LL dp[N][2], DP[N][2], sum[N]; vector<int> cir[N]; struct Edge { int to, nx; } edge[N << 1]; void add(int u, int v) { edge[tot].to = v; edge[tot].nx = head[u]; head[u] = tot++; } void getCir(int u) { vis[u] = -1; stk[++top] = u; int nx = f[u]; if(vis[nx] == 0) { getCir(nx); } else if(vis[nx] == -1) { m++; for(int i = top; i >= 1; i--) { cir[m].push_back(stk[i]); is[stk[i]] = true; if(stk[i] == nx) break; } reverse(cir[m].begin(), cir[m].end()); } top--; vis[u] = 1; } void dfs(int u) { for(int i = head[u]; ~i; i = edge[i].nx) { int v = edge[i].to; if(is[v]) continue; dfs(v); sum[u] += dp[v][0]; } dp[u][1] = p[u] + sum[u]; dp[u][0] = p[u] - d[u] + sum[u]; for(int i = head[u]; ~i; i = edge[i].nx) { int v = edge[i].to; if(is[v]) continue; dp[u][0] = min(dp[u][0], sum[u] - dp[v][0] + dp[v][1]); } } int main() { memset(head, -1, sizeof(head)); scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%d", &p[i]); for(int i = 1; i <= n; i++) scanf("%d", &d[i]); for(int i = 1; i <= n; i++) { scanf("%d", &f[i]); add(f[i], i); } for(int i = 1; i <= n; i++) { if(!vis[i]) { top = 0; getCir(i); } } LL ans = 0; for(int i = 1; i <= m; i++) { for(int j = 0; j < cir[i].size(); j++) { int root = cir[i][j]; dfs(root); } LL tmp = INF; DP[0][0] = dp[cir[i][0]][0]; DP[0][1] = dp[cir[i][0]][1]; for(int j = 1; j < cir[i].size(); j++) { int id = cir[i][j]; DP[j][0] = DP[j - 1][0] + dp[id][0]; DP[j][0] = min(DP[j][0], DP[j - 1][1] + sum[id]); DP[j][1] = DP[j - 1][0] + sum[id] + p[id]; } tmp = min(tmp, DP[cir[i].size() - 1][0]); DP[0][0] = sum[cir[i][0]]; DP[0][1] = dp[cir[i][0]][1]; for(int j = 1; j < cir[i].size(); j++) { int id = cir[i][j]; DP[j][0] = DP[j - 1][0] + dp[id][0]; DP[j][0] = min(DP[j][0], DP[j - 1][1] + sum[id]); DP[j][1] = DP[j - 1][0] + sum[id] + p[id]; } tmp = min(tmp, DP[cir[i].size() - 1][1]); ans += tmp; } printf("%lld\n", ans); return 0; } /* */