牛客周赛 Round 25

A.小红购物

题目描述

小红去淘宝买了n件物品,第i件物品价格是ai,其中部分物品小红不满意选择退货,退货可以原价退但需要收取max(5,⌊ai / 100⌋)的运费。小红想知道自己最终花费了多少钱。

⌊x⌋代表对 x 向下取整。例如:⌊3.7⌋=3

输入描述:

第一行输入一共整数n代表小红购买了n件物品。
第二行输入n正整数ai,代表n件物品的价格。
第三行输入一个长度为n且仅包含'T'和’F‘的字符串,'T'代表购买,'F'代表退货。

1≤n≤1e3

1≤ai≤1e9

输出描述:

输出一共整数,代表小红最终花费的金额。

示例1

输入

5
100 1000 200 700 800
FFTTT

输出

1715

说明

第一件退货,5元,

第二件退货,10元,

第三件购买,200元,

第四件购买,700元,

第五件购买,800元。

解题思路:

根据题目描述直接计算即可

cpp代码如下

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

typedef long long LL;

const int N = 1e3 + 10;

int T, n, m, k;
int a[N];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    string s;
    cin>>s;
    LL ans=0;
    for(int i=1;i<=n;i++)
    {
        if(s[i-1]=='T')ans+=a[i];
        else {
            ans+=max(5,a[i]/100);
        }
    }
    cout<<ans<<endl;
    return 0;
}

B.小红吃桃

题目描述

小红有许多桃子,每个桃子有一个酸度和一个甜度。小红有一个魔法:选择一个桃子,交换它的甜度和酸度。
小红可以进行任意次魔法,她想使得所有桃子甜度乘积和酸度乘积之和尽可能大。你能帮帮她吗?
请你帮小红计算最终的最大值,答案对1e9+7取模。

输入描述:

第一行输入一个正整数n,代表桃子的数量。
第二行输入n个正整数ai​,代表每个桃子的酸度。
第三行输入n个正整数bi​,代表每个桃子的甜度。
1≤n,ai,bi≤1e5

输出描述:

一个整数,代表甜度乘积和酸度乘积之和的最大值,对1e9+7取模的结果。

示例1

输入

2
1 2
2 1

输出

5

说明

交换第二个桃子的酸度和甜度,此时两个桃子酸度都是1,甜度都是2,最终答案为1*1+2*2=5。

解题思路

我们知道的是如果存在2*n个数,要将这2*n个数分为俩边,每一边都有n个数,对于每边的n个数先相乘,再将俩个乘积相加,那么怎么分能让和最大呢?肯定是大的和大的分在一起,小的和小的分到一起最终和最大,我们对于每个a[i],b[i]把俩个数中的较大值放在a[i],较小值放在b[i]即可,然后直接计算就是答案。

cpp代码如下

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

typedef long long LL;

const int N = 1e5 + 10,mod=1e9+7;

int T, n, m, k;
int a[N],b[N];

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)scanf("%d",&b[i]);
    for(int i=1;i<=n;i++)
    {
        if(a[i]<b[i])swap(a[i],b[i]);
    }
    LL v1=1,v2=1;
    for(int i=1;i<=n;i++)
    {
        v1*=a[i];
        v1%=mod;
        v2*=b[i];
        v2%=mod;
    }
    cout<<(v1+v2)%mod<<endl;
    return 0;
}

C.小红的踏前斩

题目描述

有n个怪物排成一排,第i个怪物的血量为ai。小红有两个技能可以打怪:
1. 强力攻击,消耗1mp,对一只怪物造成1点伤害。
2. 踏前斩,消耗5mp,对当前怪物造成1的伤害,同时剑气将波及后两个怪物,对下一个怪物造成2点伤害,对下下个怪物造成3点伤害.
如果一个怪物受伤后血量小于等于0,则怪物死亡。死亡后怪物的尸体依然占据一个位置,会被踏前斩的剑气打到。
小红想知道,击杀全部怪物至少需要花费多少mp?

输入描述:

第一行输入一个正整数n,代表怪物的数量。
第二行输入n个正整数ai,代表每个怪物的血量。
1≤n≤1e5
1≤ai≤1e9

输出描述:

一个整数,代表花费的mp最小值。

示例1

输入

5
2 3 4 2 3

输出

12

说明

对第一个怪和第二个怪分别进行一次强力攻击,此时每只怪物血量为[1,2,4,2,3]
对第一个怪进行一次踏前斩,此时每只怪物血量为[0,0,1,2,3]
再对第三个怪进行一次踏前斩,消灭全部怪物。
总mp消耗为12。

示例2

输入

6
1 1 2 3 2 3

输出

11

说明

对第二怪进行一次踏前斩后,每只怪物血量为[1,0,0,0,2,3]。

随后对每个怪物进行一次强力攻击。

解题思路

为了方便描述我们将强力攻击描述为普通攻击,将踏前斩描述为大招

由于每个大招只会影响到当前位置和后俩个位置,所以我们考虑从后往前贪心,贪心的策略就是应该什么时候使用大招,首先一个普通攻击消耗1mp可以造成一点伤害,一个大招消耗5mp最多造成1+2+3点伤害,我们消耗5个mp进行五次普通攻击也可以造成五点伤害,所以当大招造成的伤害小于等于5时,我们可以考虑优先选择进行普通攻击,只有当大招造成的伤害等于1+2+3时使用大招比使用普通攻击伤害更高。

结论:根据上面的分析我们可以得到一个能够达到最优解的贪心策略,就是当大招造成的伤害等于1+2+3时才使用大招,其他的时候都使用普通攻击,具体看代码处描述

cpp代码如下

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

typedef long long LL;

const int N = 1e5 + 10;

int T, n, m, k;
int a[N];
LL f[N];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    //n小于3时,大招造成的伤害肯定小于1+2+3,所以全部选择普通攻击即可
    if(n==1)cout<<a[1]<<endl;
    else if(n==2)cout<<a[1]+a[2]<<endl;
    else {
        //n>=3,考虑从后往前贪心
        LL ans=0;
        for(int i=n-2;i>=1;i--)
        {
            /*
                对于当前位置i,可以影响到i,i+1,i+2这三个位置,
                对位置i使用大招时:
                对位置i造成1点伤害
                对位置i+1造成2点伤害
                对位置i+2造成3点伤害
                对于下面三个式子求出的d1,d2,d3取最小值就是当前位置i可以使用大招的个数
            */
            int d1=a[i],d2=a[i+1]/2,d3=a[i+2]/3;
            int v=min(min(d1,d2),d3);
            ans+=v*5; //v就是使用大招的数量,所以消耗5*v个mp
            a[i]-=v,a[i+1]-=v*2,a[i+2]-=v*3; //将消耗的怪物的血量减去,方便后面的计算
            /*
                对于位置i处大招计算完了,下一个位置是i-1了,
                第i-1个位置使用大招无法影响到i+2这个位置的怪物,
                所以对于第i+2这个位置怪物剩余的血量只能使用普通攻击来
                消耗剩余的血量,所以答案还要加上这部分
            */
            ans+=a[i+2]; 
        }
        /*
            不能在第0个位置使用大招,
            所以第一个位置和第二个位置怪物剩余的血量使用普通攻击消耗完
            所以答案还要加上这部分
            */
        ans+=a[1],ans+=a[2];
        cout<<ans<<endl;
    }
    return 0;
}

D.小红树

题目描述

小红拿到了一棵树,每个节点被染成了红色或者蓝色。
小红定义每条边的权值为:删除这条边时,形成的两个子树的同色连通块数量之差的绝对值。
小红想知道,所有边的权值之和是多少?

输入描述:

第一行输入一个正整数n,代表节点的数量。
第二哈输入一个长度为n且仅由'R'和'B'两种字符组成的字符串。第i个字符为'R'代表i号节点被染成红色,为'B'则被染成蓝色。
接下来的n−1行,每行输入两个正整数u和v,代表节点u和节点v有一条边相连。
1≤n≤200000

输出描述:

一个正整数,代表所有节点的权值之和。

示例1

输入

4
BBRR
1 2
3 2
4 1

输出

2

说明

 

解题思路:

 (1)首先计算以每个节点为根节点的子树中有多少个同色连通块,这个的话直接把树建出来,然后直接遍历一遍树就可以计算出来了。

(2)然后枚举每条边,对于每条边,当删除当前边时,会产生俩个连通块,我们需要计算的是形成的新的俩个连通块之间同色连通块的个数的差值的绝对值,把这个记作每条边的贡献,将所有边的贡献加起来就是答案。然后我们考虑删除某条边会造成什么影响呢,例如上面说明中的例子,我们认为1是整棵树的根节点,我们考虑删除1和2之间的这条边,1是2的父节点,那么删除1,2之间的这条边,就形成了俩个连通块,一个连通块是以2为根节点的子树,另一个连通块是原来的整棵树除去以2为根节点的子树,我们记cnt[i]表示以i为根节点的子树中同色连通块的数目,按道理上面那部分中同色连通块的数目就是cnt[1]-cnt[i],但是删除的这条边可能会对上面部分造成影响,那么删除1,2之间这条边并不会影响到以2为根结点的子树中同色联通块的数目,但是可能会影响到上面的那部分,如果1,2俩个结点颜色不同,那么就不会影响上面部分,如果1,2俩个结点颜色相同,那么会让上面部分中同色连通块数目比cnt[1]-cnt[i]多1,到这里就分析完了,直接枚举每条边,然后计算每条边的贡献,将所有边的贡献加起来就是答案了。

cpp代码如下

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

typedef long long LL;

const int N = 2e5 + 10,M=N*2;

int T, n, m, k;
char s[N];
int h[N],e[M],ne[M],idx;
int cnt[N];
LL ans=0;

void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
//预处理cnt数组,cnt[i]表示以i为根节点的子树中同色连通块的数目
int dfs(int u,int father)
{
    int res=1;
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(j==father)continue;
        int d=dfs(j,u);
        res+=d-1;
        if(s[u]!=s[j])res++;
    }
    cnt[u]=res;
    return res;
}

//计算每条边的贡献
void dfs2(int u,int father)
{
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(j==father)continue;
        if(s[u]==s[j]){ //颜色相同
            int d1=cnt[j],d2=cnt[1]-cnt[j]+1;
            ans+=abs(d1-d2);
        }else {  //颜色不同
            int d1=cnt[j],d2=cnt[1]-cnt[j];
            ans+=abs(d1-d2);
        }
        dfs2(j,u);
    }
}
int main()
{
    cin>>n;
    cin>>s+1;
    //链式向前星建图
    memset(h,-1,sizeof h);
    for(int i=0;i<n-1;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b),add(b,a);
    }
    
    dfs(1,-1); //预处理cnt数组
    dfs2(1,-1); //计算每条边的贡献
    cout<<ans<<endl;
    return 0;
}

总结:这场非常简单,没什么思维难度,基本就是看完题就可以写了,ak的非常快了,希望下次ak的更快.

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值