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,因为原来是环,

我们需要枚举初始位置的状态,即第一个点受不受最后一个点的影响。

牛客网暑期多校2_c++牛客网暑期多校2_c++_02
#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;
}


/*
*/
View Code