AcWing第48场周赛

题目列表

AcWing 4410. 吃鸡蛋

题目描述

小明家里有 n 个鸡蛋。

每天早上,小明都要吃 1 个鸡蛋作为早餐。

小明家里还有一个母鸡。

母鸡会在第 m,2m,3m… 天的晚上下蛋,每次只下 1 个蛋。

请问,连续多少天以后,小明就没有早餐鸡蛋吃了?

输入格式
一行两个整数 n,m。

输出格式
一个整数,表示答案。

数据范围
所有测试点满足 1≤n≤100,2≤m≤100。

输入样例1:
2 2
输出样例1:
3
样例1解释
第 1,2 天的早晨,小明可以吃原本就有的鸡蛋。

第 3 天的早晨,小明可以吃第 2 天晚上母鸡下的鸡蛋。

第 4 天的早晨,小明没有鸡蛋可以吃了。

所以,连续 3 天以后,小明就没有早餐鸡蛋可以吃了。

输入样例2:
9 3
输出样例2:
13
样例2解释
第 1∼9 天的早晨,小明可以吃原本就有的鸡蛋。

第 10,11,12 天的早晨,小明可以吃第 3,6,9 天晚上母鸡下的蛋。

第 13 天的早晨,小明可以吃第 12 天晚上母鸡下的蛋。

第 14 天的早晨,小明没有鸡蛋可以吃了。

所以,连续 13 天以后,小明就没有早餐鸡蛋可以吃了。

分析

题目大意是初始有n个鸡蛋,明天早上会消耗一个,每隔m天鸡会下一个,问可以连续吃多少天鸡蛋。如果将鸡蛋余量和天数定义为一个函数,则函数图像就是初始值是n,然后以斜率是-1的幅度减少,每到m的整数倍天鸡蛋数不变,求第一次鸡蛋余量达到0是哪一天。
由于数据范围比较小,可以按天进行模拟,用n表示鸡蛋余量,每天早上n–,然后天数res++,如果res是m的倍数n就++,直至n为0退出循环。当然也可以更快的进行模拟,开始n个鸡蛋吃n天,然后判断n天下了多少个蛋,再加上新下的蛋吃的天数,再加上吃新下的蛋吃完用的天数内下的蛋数,依次类推。麻烦的地方在于天数计算,比如n = 5,m = 3,吃5天后5天内下了一个蛋,吃到第六天又下了个,这里天数是否是m的倍数就需要存储了。

代码

按天进行模拟,节省时间,比较简单

#include <iostream>
using namespace std;
int main(){
    int n,m;
    cin>>n>>m;
    int res = 0;;
    while(n) {
        res++;
        n--;
        if(res % m == 0)    n++;
    }
    cout<<res<<endl;
    return 0;
}

不按天进行模拟

#include <iostream>
using namespace std;
int main(){
    int n,m;
    cin>>n>>m;
    int res = n,k = n / m,t = n % m;;
    while(k > 0) {
        res += k;
        int s = (t + k) / m;
        t = (t + k) % m;
        k = s;
    }
    cout<<res<<endl;
    return 0;
}

AcWing 4411. 三仙归洞

题目描述

三个倒扣着的不透明小碗排成一排。

随机挑选一个小碗,将一个小球置于碗中。

然后进行 n 次操作,编号 1∼n。

对于第 i 次操作:

如果 imod2=1,则操作内容为将位于中间的碗和位于左边的碗交换位置。
如果 imod2=0,则操作内容为将位于中间的碗和位于右边的碗交换位置。
我们不妨用 0,1,2 来表示左、中、右三个位置。

n 次操作全部完成以后,装有小球的碗位于位置 x。

请你计算,所有操作开始前,装有小球的碗所在的初始位置。

输入格式
第一行,一个整数 n。

第二行,一个整数 x。

输出格式
输出一个 0∼2 的整数,表示所有操作开始前,装有小球的碗所在的初始位置。

数据范围
前 6 个测试点满足 1≤n≤5。
所有测试点满足 1≤n≤2×109,0≤x≤2。

输入样例1:
4
2
输出样例1:
1
输入样例2:
1
1
输出样例2:
0

分析

只有两种操作,左边和中间的碗交换,中间和右边的碗交换,将三个碗从左到右编号为012,由于奇数次操作是交换左边两个,偶数次操作是交换右边两个,所以很容易得出1-6次操作后碗的顺序为102,120,210,201,021,012,也就是每六次操作后碗回到原位,所以n次操作对结果产生影响的只有n % 6,倒着模拟下就可以得出答案了。

代码

#include <iostream>
#include <algorithm>
using namespace std;
int a[3];
int main(){
    int n,x;
    cin>>n>>x;
    n %= 6;
    a[x] = 1;
    while(n) {
        if(n & 1)   swap(a[0],a[1]);
        else    swap(a[1],a[2]);
        n--;
    }
    for(int i = 0;i < 3;i++) {
        if(a[i]){
            cout<<i<<endl;
            break;
        }
    }
    return 0;
}

AcWing 4412. 构造数组

题目描述

给定一个长度为 n 的整数数组 a1,a2,…,an。

请你构造长度为 n 的整数数组 b1,b2,…,bn,要求数组 b 满足:

b1=0。
对于任意一对索引 i 和 j(1≤i,j≤n),如果 ai=aj 则 bi=bj(注意,如果 ai≠aj,则 bi 和 bj 相等与否随意)。
对于任意索引 i(i∈[1,n−1]),要么满足 bi=bi+1,要么满足 bi+1=bi+1。
请计算,一共可以构造出多少个不同的满足条件的数组 b。

由于答案可能很大,你只需要输出对 998244353 取模后的结果。

例如,如果 a=[1,2,1,2,3],则一共有 2 个满足条件的数组 b,分别是 b=[0,0,0,0,0] 和 b=[0,0,0,0,1]。

输入格式
第一行包含一个整数 n。

第二行包含 n 个整数 a1,a2,…,an。

输出格式
一个整数,表示对 998244353 取模后的结果。

数据范围
前 3 个测试点满足 2≤n≤5。
所有测试点满足 2≤n≤2×105,1≤ai≤109

输入样例1:
5
1 2 1 2 3
输出样例1:
2
输入样例2:
2
100 1
输出样例2:
2
输入样例3:
4
1 3 3 7
输出样例3:
4

分析

a数组的唯一左右就是限制b数组的两个位置上的数是否相等,并且b数组中右边的数减去左边相邻的数只能是0或者1.模拟以下用例可以帮助理解题意,a数组是1 2 1 2 3,b1 = 0,由于a1 = a3,所以b1 = b3 = 0,由于b2不小于b1且不大于b3,所以b2也只能是0,同理b2 = b4,所以b3 = b2 = 0,也就推出b1到b4都是0,b5是0或者1。用例让我们理解了三点:

  • 如果bl = br,由于b中每个数都不小于其左边的数,不大于其右边的数,所以l到r中间所有数都相等。
  • 相等区间可以合并,比如[1, 3]和[2, 4]区间内的数都相等,则[1,4]区间内的数都相等。
  • 每个相等区间除了最开头的区间都有两种可能的取值。
    这样一来,原问题就转化成了若干个区间的合并问题,只要知道了合并后有多少个区间,就知道了构造b数组的方案数。比如合并后得到的区间是1,[2,5],6,[7,8],单个数也可以视为一个长度为1的区间,一共有4个区间,除了开头的区间内数只能是0,剩下区间内的数均有两种可能取值,一共有23 = 8种取值。
    另外,由于本题被y总设置了卡unordered_map,所以定义时需要先初始化一下,防止TLE。

代码

#include <cstdio>
#include <algorithm>
#include <unordered_map>
using namespace std;
const int N = 200005, mod = 998244353;
unordered_map<int,int> m(300000);
pair<int,int> q[N];
int a[N];
int main(){
    int n;
    scanf("%d",&n);
    int t = 0;
    for(int i = 0;i < n;i++){
        scanf("%d",&a[i]);
        if(m.count(a[i])) {
            q[t++] = {m[a[i]],i};//重复元素区间
        }
        else{
            m[a[i]] = i;//m记录了a[i]第一次出现的位置
            q[t++] = {i,i};//单个元素也视为一个区间,可参与合并
        }    
    }
    sort(q,q + t);
    int l = q[0].first,r = q[0].second,cnt = 1;
    for(int i = 1;i < t;i++) {
        int x = q[i].first,y = q[i].second;
        if(x <= r)  r = max(r,y);
        else{
            cnt++;
            l = x,r = y;
        }
    }
    int ans = 1;
    for(int i = 1;i < cnt;i++)  ans = ans * 2 % mod;
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值