Codeforces Round 935 (Div. 3) F. Kirill and Mushrooms【枚举+贪心+优先队列】

原题链接:https://codeforces.com/problemset/problem/1945/F

题目描述

当营地里的每个人都进入梦乡后,基里尔便偷偷溜出帐篷,到智慧的橡树下采蘑菇。

众所周知,橡树下生长着 n 朵蘑菇,每朵蘑菇都有 vi​ 的魔力。 基里尔非常想用这些蘑菇制作一种魔力最大的灵药。

灵药的强度等于其中蘑菇的数量与这些蘑菇中最小魔力的乘积。要配制灵药,基里尔要依次采摘生长在橡树下的蘑菇。基里尔可以按照任何顺序采集蘑菇。

然而,事情并非如此简单。智慧的橡树给出了一个排列 p1​,p2​,...,pn​,如果基里尔只采摘 k 朵蘑菇,那么 vp1​​,vp2​​,...,vpk−1​​ 都将变为 0。 基里尔不会使用魔力为零的蘑菇来配制灵药。

你的任务是帮助基里尔采集蘑菇,使他能够酿造出最大魔力的灵药。然而,基里尔有点害怕在橡树旁待太久,所以在所有适合采集蘑菇的方案中,他要求你找到蘑菇数量最少的那个。

输入格式

每个数据包含多个测试用例。

第一行包含一个整数 t ( 1≤t≤10^4 ) ,表示测试用例的数量。

每个测试用例的第一行都包含一个整数 n(1≤n≤2⋅10^5),表示蘑菇的数量。

第二行包含一个大小为 n 的数组 v(1≤vi​≤10^9),表示蘑菇的魔力。

第三行包含一个长度为 n 的排列 p。

保证所有测试用例中的 n 的值之和不超过 2⋅10^5。

输出格式

对于每个测试用例,输出两个用空格隔开的整数,分别为可以酿造的灵药的最大浓度和基里尔为此需要使用的最少蘑菇数量。

提示

在样例的第一个测试用例中,你需要采摘前两朵蘑菇,因此灵药的魔力等于 2⋅min(a1​,a2​)=2⋅min(9,8)=2⋅8=16。 请注意,采摘两朵蘑菇后,第三朵蘑菇的魔力将变为 0。

输入输出样例
输入 
6
3
9 8 14
3 2 1
5
1 2 3 4 5
1 2 3 4 5
6
1 2 3 4 5 6
6 5 4 3 2 1
5
1 4 6 10 10
2 1 4 5 3
4
2 2 5 5
4 2 3 1
5
1 2 9 10 10
1 4 2 3 5
输出 
16 2
9 3
8 2
20 2
5 1
20 2

解题思路:

首先更形象的描述一下题意,假设我们选择k个数,那么就会有k-1数变为0并且是p数组前k-1位置描述的k-1个位置变为0,那么对于v(p(0)),v(p(1)),...v(p(k-1))就会全部变成0,根据题意,变成0之后就不能选择了,然后我们就只能从除了这k-1个位置之外的其他位置选择数了,然后要求我们在能获取最大魔力的基础上,选择最少的位置。

实际上我们每次枚举选择k个数之后,删除的k-1个数应该是p数组前k-1位置所描述的位置的数,也就是我们选择的数实际上只能从p数组前k-1个位置之后的所有位置中选择,根据题意我们知道最大魔力=选择的数的数量*选择的数中魔力最小的那个蘑菇的魔力,我们枚举选择了k个数,实际上就是要维护可以选择的那些数中最大的k个数,那么实际上就是需要维护一个大小为k的小根堆,用于维护后面可以选择的数中最大的k个数,此时选择的数中的最小的元素就是堆顶元素,为了方便处理,我们考虑从大到小枚举要选择的元素个数i,那么堆里面的元素个数除了每次加入新的元素v[p[i]]之外,由于我们枚举的是选择i个元素,所以我们的堆中最多只能有i个元素,为了保证魔力更大,我们应该把堆中更小的多余出来的元素删除,也就是每次删除堆顶元素,直到堆中元素个数小于等于i,然后此时堆中剩余的元素就是我们要选择的i个元素,堆顶元素就是这一堆数中的最小值,然后判断一下更新答案即可。

时间复杂度:O(n),n表示数组长度,每个元素最多只会进堆出堆一次。

空间复杂度:O(n),需要一个优先队列来维护这个一过程。

cpp代码如下:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;
typedef long long LL;

const int N=2e5+10;

int T,n;
int v[N],p[N];
priority_queue<int,vector<int>,greater<int>>pq;

void solve()
{
    while(!pq.empty())pq.pop();  //多测,记得清空全局优先队列
    cin>>n;
    for(int i=1;i<=n;i++)cin>>v[i];
    for(int i=1;i<=n;i++)cin>>p[i];

    LL ans=0;
    int x=0;
    for(int i=n;i>0;i--)
    {
        pq.push(v[p[i]]);
        while(pq.size()>i){  //删除多余的元素
            pq.pop();
        }
        // 只有当删除的元素个数足够i-1,那么选择i个元素才是合法的,否则删除的元素不够,就不能选择i个元素
        //在保证魔力最大的基础上选择更少的位置
        if(pq.size()==i && (LL)pq.top()*i>=ans){  
            ans=(LL)pq.top()*i;
            x=i;
        }
    }
    cout<<ans<<' '<<x<<'\n';
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>T;
    while(T--)
    {
        solve();
    }
    return 0;
}
  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Codeforces Round 894 (Div. 3) 是一个Codeforces举办的比赛,是第894轮的Div. 3级别比赛。它包含了一系列题目,其中包括题目E. Kolya and Movie Theatre。 根据题目描述,E. Kolya and Movie Theatre问题要求我们给定两个字符串,通过三种操作来让字符串a等于字符串b。这三种操作分别为:交换a中相同位置的字符、交换a中对称位置的字符、交换b中对称位置的字符。我们需要先进行一次预处理,替换a中的字符,然后进行上述三种操作,最终得到a等于b的结果。我们需要计算预处理操作的次数。 根据引用的讨论,当且仅当b[i]==b[n-i-1]时,如果a[i]!=a[n-i-1],需要进行一次操作;否则不需要操作。所以我们可以遍历字符串b的前半部分,判断对应位置的字符是否与后半部分对称,并统计需要进行操作的次数。 以上就是Codeforces Round 894 (Div. 3)的简要说明和题目E. Kolya and Movie Theatre的要求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Codeforces Round #498 (Div. 3) (A+B+C+D+E+F)](https://blog.csdn.net/qq_46030630/article/details/108804114)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [Codeforces Round 894 (Div. 3)A~E题解](https://blog.csdn.net/gyeolhada/article/details/132491891)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值