ZROI 2018 ZYB和售货机(goods)

题目描述

可爱的 Z Y B ZYB ZYB来到一个售货机前。

售货机里有一共有 N ( ≤ 1 0 5 ) N(≤10^5) N(105)个物品,每个物品有 A i A_{i} Ai个。
自然,还有 N N N个购买按钮。正常情况下,按下第i个按钮,需要支付 C i C_{i} Ci的钱,然后会跳出一份物品 i i i
如果该物品卖完了,按下此按钮无效。

但是,这台售货机的电路连接出了点问题。第i个按钮的“弹出电路”连向了物品fi。

假设按下了第 i i i个按钮,售货机会按以下逻辑执行:

  1. 判断第 i i i个物品是否为空。

  2. 如果是,不执行任何操作,退出该购买程序。

  3. 否则,要求支付 C i C_{i} Ci的钱。

  4. 因为电路坏了,实际弹出的物品会是 f i f_{i} fi

注意:如果物品 f i f_{i} fi为空,显然也不会有物品弹出。

Z Y B ZYB ZYB很快发现了售货机的秘密,并精确掌握了 f i f_{i} fi的值。他又去调查了每一种物品的市场价。即他可以以 D i D_{i} Di的价格卖掉物品 i i i

现在 Z Y B ZYB ZYB他想通过这台售货机,赚尽量多的钱。

假设 Z Y B ZYB ZYB有足够多的成本钱。

输入

第一行一个数 N N N,表示售货机里的物品总数。
接下来有 N N N行,每行有四个数 f i , C i , D i , A i fi,Ci,Di,Ai fi,Ci,Di,Ai,意义同上。

输出
输出一个数表示最大获利。

样例输入

样例输入1
3
1 2 3 1
2 3 4 1
3 4 5 1
样例输入2
3
2 2 3 8
3 1 5 6
1 4 4 7

样例输出

样例输出1
3
样例输出2
39

提示

10 % 10\% 10% N ≤ 5 , ∏ i = N ( A i + 1 ) ≤ 105 N≤5,∏i=N(A_{i}+1)≤105 N5,i=N(Ai+1)105
30 % 30\% 30% N ≤ 10 N≤10 N10
50 % 50\% 50% N ≤ 200 N≤200 N200
另有 10 % 10\% 10% f i = i f_{i}=i fi=i
另有 10 % 10\% 10% f i ≤ i f_{i}≤i fii
另有 10 % 10\% 10% a i = 1 a_{i}=1 ai=1
100 % 100\% 100% 1 ≤ N ≤ 1 0 5 , 1 ≤ f i ≤ N , C i ≤ D i , 1 ≤ C i , D i , A i ≤ 1 0 6 1≤N≤10^5,1≤f_{i}≤N,C{i}≤D_{i},1≤C_{i},D_{i}, A_{i}≤10^6 1N105,1fiN,CiDi,1Ci,Di,Ai106

题解
考虑先建图,由 i i i连向 f i f_{i} fi,出来是个边数与点数相同的基环内向树。
于是考虑搞掉那个环。
经过深思熟虑可得,如果存在环上的一个点 x x x,按下这个按钮获得的收益不如另一条连向 f x f_{x} fx 的树边,
  如果不存在这样的点,那么我们就找一个环边价值和树边价值的差最小的点,强制它不能选择环边,统计答案即可。
  实际上,我们只要记录到每个节点的最大和次大的边。 D F S DFS DFS过程中,默认接上最大边的值,维护最大边和次大边的差的最小值。找到环之后,我们显然要断掉环——使连向某个点的边改变(从默认的最大变成次大),此时只要减去维护的最小值即可。

              


?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
 
#include<bits/stdc++.h>
using namespace std;
#define in inline
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define repd(i,a,b) for(int i=a;i>=b;i--)
#define For(i,a,b) for(int i=a;i<b;i++)
#define _(d) while(d(isdigit(ch=getchar())))
template<class T>in void g(T&t){T x,f=1;char ch;_(!)ch=='-'?f=-1:f;x=ch-48;_()x=x*10+ch-48;t=f*x;}
const int N=1e6+4;
typedef long long ll;
int c[N],a[N],d[N],v[N],smx[N],fa[N],mx[N];
int pos,dfn[N],mn,n; ll ans;
in void dfs(int x){
    if(dfn[x]==pos){ans-=mn;return;}
    if(dfn[x]) return;
    dfn[x]=pos;
    if(mx[x]){
        ans+=1ll*v[mx[x]]*a[x];
        mn=min(mn,v[mx[x]]-v[smx[x]]);
        if(mx[x]!=x) dfs(mx[x]);
    }
}
int main(){
    //freopen(".in","r",stdin);freopen(".out","w",stdout);
    g(n);
    rep(i,1,n) g(fa[i]),g(c[i]),g(d[i]),g(a[i]);
    rep(i,1,n){
        v[i]=d[fa[i]]-c[i];
        if(v[i]<0) continue;
        if(v[i]>v[mx[fa[i]]]){
            smx[fa[i]]=mx[fa[i]];
            mx[fa[i]]=i;
        }
        else if(v[i]>v[smx[fa[i]]]) smx[fa[i]]=i;
    }
    rep(i,1,n){
        if(!dfn[i]) 
            mn=1e9,++pos,dfs(i);
    }
    printf("%lld\n",ans);
    return 0;
}
 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

可爱の小公举

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值