Acwing 第 157 场周赛题解(ABC)

补一下Acwing第157场周赛,本场与传统一样easy+mid+hard各一道,话不多说,开始上题解


目录

A题:AcWing 5838. 四舍五入

题面描述

思路分析 

 代码实现

B题:AcWing 5839. 四舍五入II 

题面描述

思路分析 

代码实现

C题:AcWing 5840. 封印序列

题面描述

思路分析

代码实现

1.线段树

2.并查集

A题:AcWing 5838. 四舍五入

题面描述

给定一个两位正整数 n,请你输出其四舍五入到十位后的结果。

输入格式

一个正整数 n。

输出格式

一个整数,表示 n 四舍五入到十位后的结果。

数据范围

所有测试点满足 10≤n≤99

输入样例1:

13

输出样例1:

10

输入样例2:

98

输出样例2:

100

思路分析 

签到题,从样例中我们就能很容易发现输入n后,n再对10取模进行判断

  1. 大于等于5表示要往更大的数入,于是n本身减去取模数再加上10即可
  2. 小于5表示要往小的数舍,于是n本身只减去取模数即可

 代码实现

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
void solve()
{
    int n;
    cin>>n;
    if(n % 10 >= 5) n = n - n % 10 + 10;
    else n = n - n % 10;
    cout<<n<<endl;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    int t = 1;
    while(t--) solve();
    return 0;
}

B题:AcWing 5839. 四舍五入II 

题面描述

给定一个实数,保证该数为正数,其小数点后至少包含一位非零的数字,且小数点后不含多余末尾 0。

如果将该实数转化为字符串,则字符串的长度为 n。

现在,你可以对该实数进行不超过 t 次(也可以零次)四舍五入操作。

每次操作可以将当前数四舍五入到小数点后的任意位置(也可以四舍五入到最接近的整数)。

例如,1.645 四舍五入到小数点后两位可以得到 1.65,1.141 四舍五入到小数点后一位可以得到 1.1,3.762 四舍五入到最接近的整数可以得到 4。

我们希望通过四舍五入操作,将给定实数变得尽可能大。

请你输出通过上述操作可以得到的最大可能值。

输入格式

第一行包含两个整数 n,t。

第二行包含给定正实数,保证其既包含整数部分也包含小数部分,且小数点后至少包含一位非零的数字,且小数点后不含多余末尾 0。如果将该实数转化为字符串,则字符串的长度为 n。

输出格式

输出一个实数,表示可以得到的最大可能值。

输出答案不应包含小数点后多余末尾 0。

如果答案是整数,则直接输出整数。

数据范围

前 4 个测试点满足 1≤n≤12,1≤t≤100。
所有测试点满足 1≤n≤2×10^5,1≤t≤10^9。

输入样例1:

6 1
10.245

输出样例1:

10.25

输入样例2:

6 2
10.245

输出样例2:

10.3

输入样例3:

3 100
9.2

输出样例3:

9.2

思路分析 

tag:贪心+模拟 O(n)时间复杂度

  1. 整数部分:不会四舍五入,但会有进位的情况
  2. 小数部分:从前往后找到第一个可以四舍五入的地方(即>=5)开始从后往前推
  3. 因为越靠近小数点四舍五入后就越大,之后从后往前推是因为会有四舍五入后前一位>=5的情况。

本题要注意太多边界情况,少判断一个都会WA掉,因此一定要想清楚所有边界条件,已在代码中详细注释 --------

代码实现

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
void solve()
{
    int n,m;  cin>>n>>m;
    string s; cin>>s;
    int dot = s.find('.');//找到小数点的位置
    int k = dot + 1;
    while(k<s.size()&&s[k]<'5') k++;//找到第一个大于5的地方,然后往前枚举位置
    if(k==s.size()) cout<<s<<endl;
    //如果遍历完k等于s的长度表示小数部分都小于5直接输出,表示不能进位
    else
    {
       s = s.substr(0,k);
       //先截取这样可以避免后面不好截了,如样例1
       //从0的位置截取长度为k的字符串,刚好可以截到k上一个
       k--;m--;//次数位置都减1
       while(k>=0)
       {
           if(s[k]!='.')
           {
               s[k]++;
               if(s[k]<'5') break;//小于5表示不能往前进位了
               //大于等于5小于等于9表示还能进,但是要注意给的次数或者在整数部分就不能进了
               if(s[k] <= '9') 
               {
                   if(!m||k<dot) break;
                   s[k] = '0';
                   m--;
               }
              else s[k]='0';//要是在整数部分加一之后是10就直接变成0
             /* 如样例7 1000000000
                    239.923*/
           }
           k--;
       }

    if(k<0) s = "1"+ s; //例如999.5进位为1000
    while(s.back()=='0') s.pop_back();//去除末尾0,如样例二不去除就是10.30
    if(s.back()=='.') s.pop_back();//如999.5,不去除就是999.
    cout<<s<<endl;
    }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    int t = 1;
    while(t--) solve();
    return 0;
}

C题:AcWing 5840. 封印序列

题面描述

给定一个长度为 n 的非负整数序列 a1,a2,…,an。

其中的所有元素将被逐个封印。

具体封印顺序可以用一个 1∼n 的排列 b1,b2,…,bn 来描述,第 i 个被封印的元素即为abi。

你需要完成 n 个任务(编号 1∼n),其中第 i 个任务是:对于完成前 i 次封印的序列,请你找到序列中的一个连续子序列(可以为空),使得该子序列不含任何被封印的元素,且子序列内各元素之和尽可能大,输出这个子序列元素和的最大可能值。

空序列的元素和视为 0。

输入格式

第一行包含整数 n。

第二行包含 a1,a2,…,an。

第三行包含 b1,b2,…,bn。

输出格式

共 n 行,第 i 行输出第 i 个任务的结果。

数据范围

前 4 个测试点满足 1≤n≤10。
所有测试点满足 1≤n≤10^5,0≤ai≤10^9,b1∼bn 是一个 1∼n 的排列。

输入样例1:

4
1 3 2 5
3 4 1 2

输出样例1:

5
4
3
0

输入样例2:

5
1 2 3 4 5
4 2 3 5 1

输出样例2:

6
5
5
1
0

输入样例3:

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

输出样例3:

18
16
11
8
8
6
6
0

思路分析

笔者看到这题的最好方法是序列化并查集,可惜还不会这个,之后去补一下这个tag,先贴个序列化并查集的模版题在这——疯狂的馒头,但这题一样可以用线段树来做,只需会套线段树的模版就行,关于线段树可以移步很多帖子或者视频学习,就不细说了,也贴一个本题很像的线段树tag原题  ——你能回答这些问题吗 ,都是经典的好题,明白一题做这个问题应该就不大了,下面分别给出线段树和并查集的解法——

代码实现

1.线段树

#include<iostream>
#include<cstdio>
#include<algorithm>
#define int long long
#define x first 
#define y second 
const int N = 1e5+10;
using namespace std;
int w[N],b[N];
struct node{
     int l,r;
     int sum,lmax,rmax,tmax;
}tr[N * 4];
void pushup(node &u,node &l,node &r)
{
    u.sum = l.sum + r.sum;
    u.lmax = max(l.lmax,l.sum + r.lmax);
    u.rmax = max(r.rmax,r.sum + l.rmax);
    u.tmax = max(l.rmax + r.lmax,max(l.tmax,r.tmax));
}
void pushup(int u)
{
    pushup(tr[u],tr[u << 1],tr[u << 1 | 1]);
}
void build(int u,int l,int r)
{
    if(l == r) tr[u] = {l,r,w[r],w[r],w[r],w[r]};
    else
    {
        tr[u] = {l, r};                 // 设当前节点区间为[l, r]
        int mid = l + r >> 1;
        build(u << 1, l, mid);          // 建立左子树
        build(u << 1 | 1, mid + 1, r);  // 建立右子树
        pushup(u);                      // 修改父节点
    }
}
void modify(int u, int x, int v) {
    if (tr[u].l == x && tr[u].r == x) tr[u] = {x, x, v, v, v, v};   // 找到了
    else {
        int mid = tr[u].l + tr[u].r >> 1;
        if (x <= mid) modify(u << 1, x, v); // x位于当前区间的左半子区间
        else modify(u << 1 | 1, x, v);      // x位于当前区间的右半子区间
        pushup(u);                          // 修改父节点的相关信息
    }
}
node query(int u, int l, int r) {   // 从节点u开始,查找区间[l, r]的信息
    // 1. 包含在区间内
    //      Tl-----Tr
    //   L-------------R  
    if (tr[u].l >= l && tr[u].r <= r) return tr[u];

    int mid = tr[u].l + tr[u].r >> 1;

    // 2. 在当前的左半区间
    //    Tl-----m-----Tr
    //      L---R
    if (r <= mid) return query(u << 1, l, r);

    // 3. 在当前的右半区间
    //    Tl-----m-----Tr
    //              L-----R
    else if (l > mid) return query(u << 1 | 1, l, r);

    // 4. 两边都有,都查询
    //     Tl----m----Tr
    //        L-----R 
    else {
        auto left = query(u << 1, l, r);
        auto right = query(u << 1 | 1, l, r);
        node res;
        // 合并答案
        pushup(res, left, right);
        return res;
    }
}

void solve()
{
     int n; cin>>n;
     for(int i = 1; i <= n; i++) cin>>w[i];
     build(1,1,n);
     for(int i = 1;i <= n; i++)
     {
        cin >> b[i];
        modify(1,b[i],-1e14);
        cout<<max(0ll,query(1,1,n).tmax)<<endl;
     }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    int t = 1;
    while(t--) solve();
    return 0;
}

2.并查集

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 100010;

int n;
int a[N], b[N], p[N];
bool st[N];
LL s[N], ans[N];

int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int main()
{
    scanf("%d", &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 ++ ) p[i] = i;

    for (int i = n; i; i -- )
    {
        int k = b[i];
        st[k] = true, s[k] = a[k];
        if (st[k + 1]) s[find(k + 1)] += s[k], p[k] = find(k + 1);
        if (st[k - 1]) s[find(k)] += s[k - 1], p[k - 1] = find(k);
        ans[i - 1] = max(ans[i], s[find(k)]);
    }

    for (int i = 1; i <= n; i ++ ) printf("%lld\n", ans[i]);

    return 0;
}

好了,本篇题解分享就到这里了,下期见~~~~

  • 11
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值