18牛客多校2B discount

S o u r c e : 牛客网暑期ACM多校训练营(第二场)
P r o b l e m : n种物品,两种买法,一种选择折扣价,一种选择原价购买,使得自己的父亲可以免费。求最小值买下全部物品。
I d e a : 基环内向树DP。先找到树上的环,对于环上的每个点悬挂的树都跑一次树DP。然后断环为链,跑一次链DP。链上考虑两种情况,断开的边为u->v,分别考虑u对v有贡献和u对v无贡献,之后v->v1->…->u DP得到对应的最小值,即circle[u][1]和circle[u][0]。
比赛的时候现学基环内向树DP失败
C o d e :

#include<bits/stdc++.h>
using namespace std;

#define lc o<<1
#define rc o<<1|1
#define fi first
#define se second
#define pb push_back
#define ALL(X) (X).begin(), (X).end()
#define bcnt(X) __builtin_popcountll(X)
#define CLR(A, X) memset(A, X, sizeof(A))
#pragma comment(linker, "/STACK:1024000000,1024000000")
#define DEBUG printf("Passing [%s] in Line %d\n",__FUNCTION__,__LINE__)

using DB = double;
using LL = long long;
using PII = pair<int, int>;
const int N = 1e5+10;
const LL INF = 1e18;

bool cir[N];
int p[N], d[N], f[N], vis[N];
LL dp[N][2], c[N][2], sum[N];
vector<int> G[N], g;

void dfs(int u, int fa) {
    vis[u] = 1;
    LL sum = 0, tmp = INF;
    for(int v:G[u]) if(v != fa) {
        if(cir[v]) continue;
        dfs(v, u);
        sum += dp[v][0];
        tmp = min(tmp, dp[v][1]-dp[v][0]);
    }
    ::sum[u] = sum;
    dp[u][0] = min(sum+p[u]-d[u], sum+tmp);
    dp[u][1] = sum+p[u];
}
LL solve(int u) {
    while(!vis[u]) { vis[u] = 1, u = f[u]; }
    g.clear();
    while(vis[u] == 1) { vis[u] = 2, cir[u] = 1; g.pb(u), u = f[u]; }
    for(int u:g) dfs(u, 0);
    LL ret = INF;
    for(int x = 0; x < 2; x++) {
        c[0][0] = x?sum[g[0]]:dp[g[0]][0];
        c[0][1] = dp[g[0]][1];
        for(int i = 1; i < g.size(); i++) {
            c[i][1] = c[i-1][0]+dp[g[i]][1];
            c[i][0] = min(c[i-1][0]+dp[g[i]][0], c[i-1][1]+sum[g[i]]);
        }
        ret = min(ret, c[g.size()-1][x]);
    }
    return ret;
}

int main() {
    int n;
    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);
        G[f[i]].pb(i);
    }
    LL ans = 0;
    for(int i = 1; i <= n; i++) if(!vis[i]) {
        ans += solve(i);
    }
    printf("%lld\n", ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_32506797/article/details/81174876