• [JZOJ6346]:ZYB和售货机(拓扑+基环内向森林) HEOI


    题目描述

      可爱的$ZYB$来到一个售货机前。
      售货机里有一共有$N(\leqslant 10^5)$个物品,每个物品有$A_i$个。自然,还有$N$个购买按钮。正常情况下,按下第$i$个按钮,需要支付$C_i$的钱,然后会跳出一份物品$i$。如果该物品卖完了,按下此按钮无效。
      但是,这台售货机的电路连接出了点问题。第$i$个按钮的“弹出电路”连向了物品$f_i$。
      假设按下了第$i$个按钮,售货机会按以下逻辑执行:
      $1.$判断第$i$个物品是否为空。
      $2.$如果是,不执行任何操作,退出该购买程序。
      $3.$否则,要求支付$C_i$的钱。
      $4.$因为电路坏了,实际弹出的物品会是$f_i$。
      注意:如果物品$f_i$为空,显然也不会有物品弹出。
      $ZYB$很快发现了售货机的秘密,并精确掌握了$f_i$的值。他又去调查了每一种物品的市场价。即他可以以$D_i$的价格卖掉物品$i$。
      现在$ZYB$他想通过这台售货机,赚尽量多的钱。
      假设$ZYB$有足够多的成本钱。


    输入格式

      从文件$goods.in$中读入数据。
      第一行一个数$N$,表示售货机里的物品总数。
      接下来有$N$行,每行有四个数$f_i,C_i,D_i,A_i$,意义同上。


    输出格式

      输出到文件$goods.out$中。
      输出一个数表示最大获利。


    样例

    样例输入1:

    3
    1 2 3 1
    2 3 4 1
    3 4 5 1

    样例输出1:

    3

    样例输入2:

    3
    2 2 3 8
    3 1 5 6
    1 4 4 7

    样例输出2:

    39


    数据范围与提示

      前$10\%$:$N\leqslant 5,\prod\limits_{i=1}^N(A_i+1)\leqslant 10^5$
      前$30\%$:$N\leqslant 10$
      前$50\%$:$N\leqslant 200$
      另有$10\%$:$f_i=i$
      另有$10\%$:$f i\leqslant i$
      另有$10\%$:$a_i=1$
      $100\%$:$1\leqslant N\leqslant 10^5,1\leqslant f_i\leqslant N,C_i\leqslant D_i,1\leqslant C_i,D_i,A_i\leqslant 10^6$


    题解

    显然如果卖出的价格比买入的价格还低直接不买就好了。

    然后剩下的依赖关系会形成一个基环内向森林。

    可以先用拓扑处理掉不在环里的东西。

    对于每一个环,从贡献最小的那里断一定更优,注意特判环的大小为$1$的情况就好了。

    时间复杂度:$\Theta(N)$。

    期望得分:$100$分。

    实际得分:$100$分。


    代码时刻

    #include<bits/stdc++.h>
    using namespace std;
    struct rec{int nxt,to;}e[100001];
    int head[100001],cnt;
    int N;
    int f[100001],C[100001],D[100001],A[100001];
    int du[100001];
    bool vis[100001];
    vector<int>v;
    pair<int,int>g[100001][2];
    priority_queue<pair<int,int>>q[100001];
    long long ans;
    void add(int x,int y)
    {
    	e[++cnt].nxt=head[x];
    	e[cnt].to=y;
    	head[x]=cnt;
    }
    void topsort()
    {
    	queue<int>q;
    	for(int i=1;i<=N;i++)if(!du[i])q.push(i);
    	while(q.size())
    	{
    		int x=q.front();q.pop();
    		vis[x]=1;ans+=g[x][0].first;
    		for(int i=head[x];i;i=e[i].nxt)
    		{du[e[i].to]--;if(!du[e[i].to])q.push(e[i].to);}
    	}
    }
    void dfs(int x)
    {
    	v.push_back(x);vis[x]=1;
    	for(int i=head[x];i;i=e[i].nxt)if(!vis[e[i].to])dfs(e[i].to);
    }
    int main()
    {
    	scanf("%d",&N);
    	for(int i=1;i<=N;i++)scanf("%d%d%d%d",&f[i],&C[i],&D[i],&A[i]);
    	for(int i=1;i<=N;i++)if(C[i]<D[f[i]])q[f[i]].push(make_pair(D[f[i]]-C[i],i));
    	for(int i=1;i<=N;i++)
    	{
    		if(q[i].size()){g[i][0]=q[i].top();q[i].pop();}
    		if(q[i].size()){g[i][1]=q[i].top();q[i].pop();}
    		ans+=1LL*g[i][0].first*(A[i]-1);
    		if(g[i][0].second)
    		{
    			add(i,g[i][0].second);
    			du[g[i][0].second]=1;
    		}
    	}
    	topsort();
    	for(int i=1;i<=N;i++)
    	{
    		if(vis[i])continue;
    		v.clear();dfs(i);
    		if(v.size()==1){ans+=g[i][0].first;continue;}
    		int res=0x3f3f3f3f;
    		int sum=0;
    		for(int j=0;j<v.size();j++)
    		{
    			res=min(res,g[v[j]][0].first-g[v[j]][1].first);
    			sum+=g[v[j]][0].first;
    		}
    		ans+=sum-res;
    	}
    	printf("%lld",ans);
    	return 0;
    }
    

    rp++

  • 相关阅读:
    Effective Java 第三版——26. 不要使用原始类型
    Effective Java 第三版——25. 将源文件限制为单个顶级类
    Effective Java 第三版——24. 优先考虑静态成员类
    Effective Java 第三版——23. 优先使用类层次而不是标签类
    Effective Java 第三版——22. 接口仅用来定义类型
    Effective Java 第三版——21. 为后代设计接口
    Effective Java 第三版——20. 接口优于抽象类
    Effective Java 第三版——19. 如果使用继承则设计,并文档说明,否则不该使用
    Effective Java 第三版——18. 组合优于继承
    Effective Java 第三版——17. 最小化可变性
  • 原文地址:https://www.cnblogs.com/My-tiantian/p/11850043.html
Copyright © 2020-2023  润新知