AtCoder Beginner Contest 153 题解

AtCoder Beginner Contest 153 题解

这次的AtCoder明显变水了居然我都能AK。题目质量有所下降,好多板子啊(逃

今年的第一篇博客,要是肺炎来了怕不是就是最后一篇力(呸呸呸)。

A - Serval vs Monster

题意

给你\(H\)\(A\),问你多少个\(A\)\(H\)大。

做法

输出\(\lceil \frac H A \rceil\)即可,可以转化为\(\lfloor \frac {H+A-1} A \rfloor\),此处\(\lceil a \rceil\)\(\lfloor a \rfloor\)分别代表对\(a\)上取整和下取整。

程序

#include<bits/stdc++.h>
using namespace std;

int main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    int h,a;
    cin>>h>>a;
    cout<<(h+a-1)/a<<endl;

    return 0;
}

B - Common Raccoon vs Monster

题意

给你\(H\)\(N\)\(N\)个数组成的数组\(A\),问你数组里的数都减一次能不能把\(H\)减到\(0\)及以下。

做法

判断数组所有元素的和是否大于等于\(H\)即可。

程序

#include<bits/stdc++.h>
using namespace std;

int h,n;
int a[100005];

int main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin>>h>>n;
    for(int i=0;i<n;i++){
        cin>>a[i];
        h-=a[i];
    }
    if(h>0){
        cout<<"No"<<endl;
    }else{
        cout<<"Yes"<<endl;
    }

    return 0;
}

C - Fennec vs Monster

C开始不解释题目意思了,大家肯定都看得懂(就是我懒)

做法

贪心地找出HP最大的\(K\)个怪物先用技能打死,之后的只能平A了。

程序

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

int n,k;
int h[200005];
ll ans;

int main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin>>n>>k;
    for(int i=0;i<n;i++)cin>>h[i];
    sort(h,h+n);
    for(int i=0;i<n-k;i++){
        ans+=h[i];
    }
    cout<<ans<<endl;

    return 0;
}

D - Caracal vs Monster

做法

定义一个操作是对场上当前的所有怪物都攻击一次。这样场上的怪物一直都是相同的血量,例如:

  • 开始有\(1\)只HP为\(4\)的怪物
  • 攻击\(1\)次后有\(2\)只HP为\(2\)的怪物
  • 攻击\(2\)次后有\(4\)只HP为\(1\)的怪物
  • 攻击\(4\)次后所有怪物死亡

总共\(7\)次攻击。

为什么呢?方便计算啊。只要记录场上怪物的HP和总数就好了。

之后做法显然

程序

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

ll h,cnt,ans;

int main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin>>h;
    cnt=1;
    while(h){
        ans+=cnt;
        h>>=1;
        cnt<<=1;
        //>>和<<不是cincout操作符,是位运算的右移和左移,可自行查阅文档了解
        //也可替换成:
        //h/=2;
        //cnt*=2;
    }
    cout<<ans<<endl;

    return 0;
}

E - Crested Ibis vs Monster

做法

背包板子啊,AtCoder你变力。

直接DP上,定义dp[i][j]为计算完前i个咒语造成了j点伤害最少使用的MP量。那么状态转移方程是显然的:

\[dp[i][j]=\min(dp[i-1][j],dp[i][j-a[i]]+b[i]) \]

好了,问题解决了,边界条件详见程序。

程序

#include<bits/stdc++.h>
using namespace std;

int h,n;
int a[1005],b[1005];
int dp[1005][20005];

int main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin>>h>>n;
    memset(dp,0x3f,sizeof(dp));
    dp[0][0]=0;
    for(int i=0;i<n;i++){
        cin>>a[i]>>b[i];
        for(int j=0;j<=h+10000;j++){//由于可能伤害不是正好杀死,一定要预留一点伤害的量
            if(j+a[i]<=h+10000)dp[i][j+a[i]]=min(dp[i][j+a[i]],dp[i][j]+b[i]);
            dp[i+1][j]=min(dp[i+1][j],dp[i][j]);//此处为计算方便我正着dp了
        }
    }
    int ans=0x3f3f3f3f;
    for(int i=h;i<=h+10000;i++){
        ans=min(ans,dp[n][i]);
    }
    cout<<ans<<endl;

    return 0;
}

F - Silver Fox vs Monster

做法

首先,正常人都会选择把这些怪物们按坐标排个序。

然后,我们可以看出,对于每一个怪物\(A\),我们都可以找到一个它右边的怪物\(B\)\(A\)\(B\)可以被同一个炸弹炸到,但\(B\)右边的怪物不可以,这个可以用two pointers(中文是尺取法来着?)在\(O(N)\)时间里计算。可以看这篇博客了解。

我们就从左到右地循环怪物\(A\),每一次都炸很多次把它炸死。可以证明,此时炸弹位置在波及到\(A\)的情况下,越向右放越好,也就是波及了\(A\)\(B\)的所有怪物。这个用线段树维护每个怪物的血量就好了。又是板子,AtCoder出题人偷懒(

程序

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

int n,d,a,sz;
ll data[800005];
ll lazy[800005];
pair<int,int> mon[200005];

void build(int bk,int l,int r){
    if(l==r){
        if(l<=n){
            data[bk]=mon[l].second;
        }
        return;
    }
    build(bk<<1,l,(l+r)>>1);
    build(bk<<1|1,((l+r)>>1)+1,r);
    data[bk]=data[bk<<1]+data[bk<<1|1];
}

void upd(int bk,int l,int r,int ql,int qr,ll val){
    if(qr<l||r<ql)return;
    if(ql<=l&&r<=qr){
        lazy[bk]+=val;
        data[bk]+=val*(r-l+1);
        return;
    }
    lazy[bk<<1]+=lazy[bk];
    data[bk<<1]+=lazy[bk]*((r-l+1)>>1);
    lazy[bk<<1|1]+=lazy[bk];
    data[bk<<1|1]+=lazy[bk]*((r-l+1)>>1);
    lazy[bk]=0;
    upd(bk<<1,l,(l+r)>>1,ql,qr,val);
    upd(bk<<1|1,((l+r)>>1)+1,r,ql,qr,val);
    data[bk]=data[bk<<1]+data[bk<<1|1];
}

ll qry(int bk,int l,int r,int ql,int qr){
    if(qr<l||r<ql)return 0;
    if(ql<=l&&r<=qr){
        return data[bk];
    }
    lazy[bk<<1]+=lazy[bk];
    data[bk<<1]+=lazy[bk]*((r-l+1)>>1);
    lazy[bk<<1|1]+=lazy[bk];
    data[bk<<1|1]+=lazy[bk]*((r-l+1)>>1);
    lazy[bk]=0;
    return qry(bk<<1,l,(l+r)>>1,ql,qr)+qry(bk<<1|1,((l+r)>>1)+1,r,ql,qr);
}

ll ans;

int main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin>>n>>d>>a;
    sz=1;while(sz<n)sz<<=1;
    for(int i=1;i<=n;i++){
        cin>>mon[i].first>>mon[i].second;
    }
    sort(mon+1,mon+1+n);//读入,对怪物排序
    build(1,1,sz);//建立线段树
    int l=1,r=1;
    while(l<=n){
        while(mon[r+1].first-mon[l].first<=d+d&&r<n){
            r++;
        }//尺取法
        ll lft=qry(1,1,sz,l,l);//cerr<<lft<<endl;
        if(lft<=0){//如果怪物死了就不计算了,我还因为这个wa了一次qaq
            l++;
            continue;
        }
        ll tms=(lft+a-1)/a;//杀掉这个怪物需要多少炸弹
        ans+=tms;
        upd(1,1,sz,l,r,(-tms)*a);//我也不知道为什么要加上括号qaq
        l++;
    }
    cout<<ans<<endl;

    return 0;
}

感谢

感谢阅读,我写得很烂您还读完了。太感谢了qaq

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
AtCoder Beginner Contest 134 是一场 AtCoder 的入门级比赛,以下是每道题的简要题解: A - Dodecagon 题目描述:已知一个正十二边形的边长,求它的面积。 解题思路:正十二边形的内角为 $150^\circ$,因此可以将正十二边形拆分为 12 个等腰三角形,通过三角形面积公式计算面积即可。 B - Golden Apple 题目描述:有 $N$ 个苹果和 $D$ 个盘子,每个盘子最多可以装下 $2D+1$ 个苹果,求最少需要多少个盘子才能装下所有的苹果。 解题思路:每个盘子最多可以装下 $2D+1$ 个苹果,因此可以将苹果平均分配到每个盘子中,可以得到最少需要 $\lceil \frac{N}{2D+1} \rceil$ 个盘子。 C - Exception Handling 题目描述:给定一个长度为 $N$ 的整数序列 $a$,求除了第 $i$ 个数以外的最大值。 解题思路:可以使用两个变量 $m_1$ 和 $m_2$ 分别记录最大值和次大值。遍历整个序列,当当前数不是第 $i$ 个数时,更新最大值和次大值。因此,最后的结果应该是 $m_1$ 或 $m_2$ 中较小的一个。 D - Preparing Boxes 题目描述:有 $N$ 个盒子和 $M$ 个物品,第 $i$ 个盒子可以放入 $a_i$ 个物品,每个物品只能放在一个盒子中。现在需要将所有的物品放入盒子中,每次操作可以将一个盒子内的物品全部取出并分配到其他盒子中,求最少需要多少次操作才能完成任务。 解题思路:首先可以计算出所有盒子中物品的总数 $S$,然后判断是否存在一个盒子的物品数量大于 $\lceil \frac{S}{2} \rceil$,如果存在,则无法完成任务。否则,可以用贪心的思想,每次从物品数量最多的盒子中取出一个物品,放入物品数量最少的盒子中。因为每次操作都会使得物品数量最多的盒子的物品数量减少,而物品数量最少的盒子的物品数量不变或增加,因此这种贪心策略可以保证最少需要的操作次数最小。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值