zoukankan      html  css  js  c++  java
  • [JZOJ6346]:ZYB和售货机(拓扑+基环内向森林)

    题目描述

      可爱的$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\%$:$Nleqslant 5,prodlimits_{i=1}^N(A_i+1)leqslant 10^5$
      前$30\%$:$Nleqslant 10$
      前$50\%$:$Nleqslant 200$
      另有$10\%$:$f_i=i$
      另有$10\%$:$f ileqslant i$
      另有$10\%$:$a_i=1$
      $100\%$:$1leqslant Nleqslant 10^5,1leqslant f_ileqslant N,C_ileqslant D_i,1leqslant C_i,D_i,A_ileqslant 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++

  • 相关阅读:
    SPOJ GSS4 Can you answer these queries IV ——树状数组 并查集
    SPOJ GSS3 Can you answer these queries III ——线段树
    SPOJ GSS2 Can you answer these queries II ——线段树
    SPOJ GSS1 Can you answer these queries I ——线段树
    BZOJ 2178 圆的面积并 ——Simpson积分
    SPOJ CIRU The area of the union of circles ——Simpson积分
    HDU 1724 Ellipse ——Simpson积分
    HDU 1071 The area ——微积分
    HDU 4609 3-idiots ——FFT
    BZOJ 2194 快速傅立叶之二 ——FFT
  • 原文地址:https://www.cnblogs.com/wzc521/p/11850043.html
Copyright © 2011-2022 走看看