ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

2018牛客多校第三场B(基环树dp)

2021-10-06 17:01:13  阅读:154  来源: 互联网

标签:cir int res 多校 son 牛客 第三场 id dp


题目大意:

n ( ≤ 1 e 5 ) n(\le1e5) n(≤1e5)件商品,第 i i i件商品价格为 p [ i ] p[i] p[i],可以用以下两种折扣中的一种:

  • 得到 d [ i ] d[i] d[i]的折扣,价格变为 p [ i ] − d [ i ] p[i]-d[i] p[i]−d[i]
  • 原价 p [ i ] p[i] p[i]购买,免费获得 f [ i ] f[i] f[i]物品

解题思路:

  • f [ i ] → i f[i]\rightarrow i f[i]→i连边,那么就构成了基环树(树上有且仅有一个环)森林

  • 对于基环树常见的套路就是:先处理连接环的树,再拆环为链,最后考虑拆除部分的情况

  • 先看普通树应该如何处理:设 d p [ i ] [ 0 ] dp[i][0] dp[i][0]子树 i i i的最小费用, d p [ i ] [ 1 ] dp[i][1] dp[i][1]表示以第二种优惠购买商品 i i i的最小费用(免费拥有其父亲)
    d p [ i ] [ 1 ] = p [ u ] + ∑ v ∈ s o n [ u ] d p [ v ] [ 0 ] d p [ i ] [ 0 ] = m i n ( p [ u ] − d [ u ] + ∑ v ∈ s o n [ u ] d p [ v ] [ 0 ] , m i n v ′ ∈ s o n [ u ] ( d p [ v ′ ] [ 1 ] + ∑ v ∈ s o n [ u ] , v ! = v ′ d p [ v ] [ 0 ] ) ) dp[i][1]=p[u]+\sum_{v\in son[u]}dp[v][0] \\ dp[i][0]=min(p[u]-d[u]+\sum_{v\in son[u]}dp[v][0],min_{v'\in son[u]}(dp[v'][1]+\sum_{v\in son[u],v!=v'}dp[v][0])) dp[i][1]=p[u]+v∈son[u]∑​dp[v][0]dp[i][0]=min(p[u]−d[u]+v∈son[u]∑​dp[v][0],minv′∈son[u]​(dp[v′][1]+v∈son[u],v!=v′∑​dp[v][0]))

  • 对于环,先将其拆成链,设环上结点个数为 m m m, c i r [ i ] cir[i] cir[i]为链上第 i i i个结点对应的是原图中哪个结点:
    g [ i ] [ 1 ] = d p [ c i r [ i ] ] [ 1 ] + g [ i − 1 ] [ 0 ] g [ i ] [ 0 ] = m i n ( d p [ c i r [ i ] ] [ 0 ] + g [ i − 1 ] [ 0 ] , g [ i − 1 ] [ 1 ] + ∑ v ∈ s o n [ c i r [ i ] ] d p [ v ] [ 0 ] ) g[i][1] = dp[cir[i]][1]+g[i-1][0] \\ g[i][0]=min(dp[cir[i]][0]+g[i-1][0],g[i-1][1]+\sum_{v\in son[cir[i]]}dp[v][0]) g[i][1]=dp[cir[i]][1]+g[i−1][0]g[i][0]=min(dp[cir[i]][0]+g[i−1][0],g[i−1][1]+v∈son[cir[i]]∑​dp[v][0])

  • 接下来对于拆除部分,再分两种情况考虑第 m m m个结点是否对第 1 1 1个结点有赠送来初始化:
    有 赠 送 : g [ 1 ] [ 0 ] = ∑ v ∈ s o n [ c i r [ 1 ] ] d p [ v ] [ 0 ] , g [ 1 ] [ 1 ] = d p [ c i r [ 1 ] ] [ 1 ] 无 赠 送 : g [ 1 ] [ 0 ] = d p [ c i r [ 1 ] ] [ 0 ] , g [ 1 ] [ 1 ] = d p [ c i r [ 1 ] ] [ 1 ] 有赠送:g[1][0]=\sum_{v\in son[cir[1]]}dp[v][0], g[1][1]=dp[cir[1]][1] \\ 无赠送:g[1][0]=dp[cir[1]][0],g[1][1]=dp[cir[1]][1] 有赠送:g[1][0]=v∈son[cir[1]]∑​dp[v][0],g[1][1]=dp[cir[1]][1]无赠送:g[1][0]=dp[cir[1]][0],g[1][1]=dp[cir[1]][1]

AC代码:

#include <bits/stdc++.h>
#define ft first
#define sd second
#define pb push_back
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) //不能跟puts混用
#define seteps(N) fixed << setprecision(N)
#define endl "\n"
const int maxn = 1e5 + 10;
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int, int> pii;
const ll mod = 1e9 + 7;
const ll inf = 0x3f3f3f3f3f3f3f;
int n, cntc, f[maxn], cir[maxn];
ll sdp[maxn]; //sdp[i]存储的是以i为父节点的j的dp[j][0]总和
ll p[maxn], d[maxn], dp[maxn][2], g[maxn][2];
vector <int> G[maxn];
int vis[maxn];
ll ans = 0;
void solve2(int u) { //树上问题
    ll res = 0;
    dp[u][1] = p[u];
    dp[u][0] = p[u] - d[u];
    for (auto v : G[u]) {
        if (vis[v] == 2) continue;
        solve2(v);
        res += dp[v][0];
    }
    dp[u][1] += res, dp[u][0] += res;
    sdp[u] = res;
    for (auto v : G[u]) {
        if (vis[v] == 2) continue;
        vis[v]++;
        dp[u][0] = min(dp[u][0], res - dp[v][0] + dp[v][1]);
    }
}
void solve1(int id) { //
    cntc = 0;
    while (!vis[id]) vis[id]++, id = f[id];
    while (vis[f[id]] != 2) { //处理环上的点
        vis[f[id]]++;
        cir[++cntc] = id;    
        id = f[id];
    }
    for (int i = 1; i <= cntc; i++) {
        solve2(cir[i]); //解决以cir[i]为根的树上问题
    }
    // 处理环上问题:拆环为链
    /* 第一个点不是由最后一个点赠送 */
    ll res = inf;
    g[1][0] = dp[cir[1]][0], g[1][1] = dp[cir[1]][1];
    for (int i = 2; i <= cntc; i++) {
        g[i][1] = dp[cir[i]][1] + g[i - 1][0];
        g[i][0] = min(dp[cir[i]][0] + g[i - 1][0], g[i - 1][1] + sdp[cir[i]]);
    }
    res = min(res, g[cntc][0]);
    /* 第一个点是由最后一个点赠送 */
    g[1][0] = sdp[cir[1]], g[1][1] = dp[cir[1]][1];
    for (int i = 2; i <= cntc; i++) {
        g[i][1] = dp[cir[i]][1] + g[i - 1][0];
        g[i][0] = min(dp[cir[i]][0] + g[i - 1][0], g[i - 1][1] + sdp[cir[i]]);
    }
    res = min(res, g[cntc][1]);
    ans += res;
}
int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> p[i];
    for (int i = 1; i <= n; i++) cin >> d[i];
    for (int i = 1; i <= n; i++) cin >> f[i], G[f[i]].pb(i);
    for (int i = 1; i <= n; i++) {
        if (!vis[i]) 
            solve1(i);
    }
    cout << ans << endl;
    return 0;
}

标签:cir,int,res,多校,son,牛客,第三场,id,dp
来源: https://blog.csdn.net/weixin_45691711/article/details/120626305

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有