Codeforces Round #643 (Div. 2) 题解

A. Sequence with Digits

  • 题意

    定义一个序列 a a a,通项公式为 a n + 1 = a n + m a x D i g i t ( a n ) ⋅ m i n D i g i t ( a n ) a_{n+1}=a_n+maxDigit(a_n)\cdot minDigit(a_n) an+1=an+maxDigit(an)minDigit(an)。给出 a 1 a_1 a1,求 a k a_k ak

  • 解题思路

    这题既然是 A A A题,那绝对不会太难,不要被数据范围给吓到了。

    我们发现,当 a n a_n an有一位为 0 0 0的时候,那么 m a x D i g i t ( a n ) ⋅ m i n D i g i t ( a n ) maxDigit(a_n)\cdot minDigit(a_n) maxDigit(an)minDigit(an)是为 0 0 0的,即 a n + 1 = a n a_{n+1}=a_n an+1=an,所以一但出现了 0 0 0,那么后续的值都与 a n a_n an相同。那么如果我们能得知在 1 e 6 1e6 1e6以下的迭代可以得到位为 0 0 0的数,实际上这题就可以直接暴力了。

    我们可以简单分析以下,如果 m a x D i g i t ( a n ) ⋅ m i n D i g i t ( a n ) maxDigit(a_n)\cdot minDigit(a_n) maxDigit(an)minDigit(an)不为 0 0 0,那么它会不停的影响 a n a_n an,使得 a n a_n an变化,而且 m a x D i g i t ( a n ) ⋅ m i n D i g i t ( a n ) maxDigit(a_n)\cdot minDigit(a_n) maxDigit(an)minDigit(an)的最大值为 81 81 81,所以它能直接影响最后两位,这不难得知在这么多情况变化下,一定能出现 0 0 0的。

    具体证明可以参考官网解释

  • AC代码

/**
  *@filename:A
  *@author: pursuit
  *@created: 2021-09-01 10:13
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl

using namespace std;

typedef pair<int,int> pii;
typedef long long ll;
const int N = 1e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;

int t;
ll a, k;
ll cal(ll a){
    ll minn = INF, maxx = 0;
    while(a){
        minn = min(a % 10, minn);
        maxx = max(a % 10, maxx);
        a /= 10;
    }
    return minn * maxx;
}
void solve(){
    int i = 1;
    while(i < k){
        ll temp = cal(a);
        if(temp == 0)break;
        a += temp;
        ++ i;
    }
    printf("%lld\n", a);
}
int main(){	
    scanf("%d", &t);
    while(t -- ){
        scanf("%lld%lld", &a, &k);
        solve();
    }
    return 0;
}

B. Young Explorers

  • 题意

    给定一个序列 a a a,要求你进行分组,使得 i i i所处的组人数 ≥ a i \geq a_i ai。请你求出能分的最大组数。

  • 解题思路

    贪心的来看,由于加入大的值会影响组的最低人数限制,所以我们尽量是让小的和小的组合,且满足组合适就停止加人。则我们可以先对数组排序,统计当前加入组的人数 c n t cnt cnt,当 c n t ≥ a i cnt\geq a_i cntai的时候,说明组符合要求。开新组继续遍历判断。

  • AC代码

/**
  *@filename:B
  *@author: pursuit
  *@created: 2021-09-01 10:19
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl

using namespace std;

typedef pair<int,int> pii;
typedef long long ll;
const int N = 2e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;

int t, n, a[N];
//经验值为a的人只能加入a或者更多人的群组。
void solve(){
    sort(a + 1, a + 1 + n);
    int res = 0, cnt = 0;
    for(int i = 1; i <= n; ++ i){
        ++ cnt;
        if(a[i] <= cnt){
            ++ res;
            cnt = 0;
        }
    }
    printf("%d\n", res);
}
int main(){	
    scanf("%d", &t);
    while(t -- ){
        scanf("%d", &n);
        for(int i = 1; i <= n; ++ i){
            scanf("%d", &a[i]);
        }
        solve();
    }
    return 0;
}

C. Count Triangles

  • 题意

    给定 A , B , C , D A,B,C,D A,B,C,D,其中 x , y , z x,y,z x,y,z满足 A ≤ x ≤ B ≤ y ≤ C ≤ z ≤ D A\leq x\leq B \leq y \leq C \leq z \leq D AxByCzD。求 x , y , z x,y,z x,y,z组成三角形的个数。

  • 解题思路

    普通的暴力枚举是不行的,即通过 x , y x,y x,y确定 z z z的可能数,这种时间复杂度达到 O ( n 2 ) O(n^2) O(n2)

    我们继续思考,由于 x ≤ y ≤ z x\leq y\leq z xyz,所以一但 x + y > z x+y>z x+y>z,那么是一定能组成三角形的,因为 x + z > y , y + z > x x+z>y,y+z>x x+z>y,y+z>x也是一定满足的,符合组成三角形的条件。所以我们关键就是找出 x + y > z x+y>z x+y>z的所有可能数。到了这一步,我们观察 x + y x+y x+y,由于 y ∈ [ B , C ] y\in[B,C] y[B,C],所以 + x +x +x实际上就是让 [ B , C ] [B,C] [B,C]都加上 x x x,则 [ B + x , C + x ] [B+x,C+x] [B+x,C+x]的数都出现了一次。

    那这种修改我们可以通过差分数组来实现 O ( 1 ) O(1) O(1)修改,这样我们可以枚举 x x x的所有取值,将与 y y y的可能出现数利用差分统计。

    这样,我们再还原差分数组即可得到数组 n u m num num了,其中 n u m [ i ] num[i] num[i]代表 i i i出现的次数,即 x + y x+y x+y。所以,为了求出 x + y > z x+y>z x+y>z的所有可能数,我们得知晓 > z >z >z的数的所有出现次数,所以这里我们需要作前缀和。最后枚举 z z z,通过 s u m [ N − 1 ] − s u m [ z ] sum[N-1]-sum[z] sum[N1]sum[z]累加统计即可。

  • AC代码

/**
  *@filename:C
  *@author: pursuit
  *@created: 2021-09-01 10:31
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl

using namespace std;

typedef pair<int,int> pii;
typedef long long ll;
const int N = 1e6 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;

int a, b, c, d;
ll sum[N];
void solve(){
    //做差分。
    for(int i = a; i <= b; ++ i){
        ++ sum[i + b], -- sum[i + c + 1];
    }
    for(int i = 1; i < N; ++ i){
        sum[i] += sum[i - 1];
    }
    for(int i = 1; i < N; ++ i){
        sum[i] += sum[i - 1];
    }
    ll res = 0;
    for(int i = c; i <= d; ++ i){
        res += sum[N - 1] - sum[i];
    }
    printf("%lld\n", res);
}
int main(){
    scanf("%d%d%d%d", &a, &b, &c, &d);	
    solve();
    return 0;
}

D. Game With Array

  • 题意

    Petya和Vasya玩游戏,一开始Petya构造 n n n个元素的数组,总和为 s s s,现在需要Petya选择一个数 k ∈ [ 0 , s ] k\in[0,s] k[0,s],为了赢得游戏,Vasya必须从Petya构造的数组种找到一个非空子数组使得总和 = k   o r   s − k =k \ or \ s-k =k or sk。如果没有找到,Vasya就会输掉游戏。

    现在给定 n , s n,s n,s。需要你判断Petya是否能赢得游戏。如果能赢,输出构造的数组和一个选定的 k k k

  • 解题思路

    注意到 k k k是由Petya选定的,所以唯一一种可能输就是选定的所有 k k k都可以构造出来。那么什么时候所有 k k k都可以构造出来呢?即当平均值小于 2 2 2的时候,即 2 n > s 2n>s 2n>s,因为这种情况我们能肯定数组的最小值和最大值是多少,同时根据数的组合能将所有的数表示出来,例如 n = 4 , s = 7 n=4,s=7 n=4,s=7,那么我们构造的数组只有 1 , 1 , 1 , 4 1,1,1,4 1,1,1,4或者 2 , 2 , 2 , 1 2,2,2,1 2,2,2,1等等,我们发现,这种构造都是不符合的,详细证明可以参考官方解答

    那么既然平均值是 ≥ 2 \geq 2 2的,所以我们可以构造出没有 1 1 1的数组,然后 k k k 1 1 1即可。

  • AC代码

/**
  *@filename:D
  *@author: pursuit
  *@created: 2021-09-01 10:55
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl

using namespace std;

typedef pair<int,int> pii;
typedef long long ll;
const int N = 1e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;

int n, s, k;
/*
n个整数, 数组的元素总和为s。Petya选择一个整数k。
找到一个非空子数组,使得总和为s或者k - s。
*/
void solve(){
    if(2 * n > s){
        puts("NO");
    }
    else{
        puts("YES");
        for(int i = 0; i < n - 1; ++ i){
            printf("2 ");
        }
        printf("%d\n", s - (n - 1) * 2);
        puts("1");
    }
}
int main(){	
    scanf("%d%d", &n, &s);
    solve();
    return 0;
}

E. Restorer Distance

  • 题意

    一开始给定 n n n个柱子的高度,现在给定三种操作:

    • 将砖块放到一个柱子上,花费 A A A,高度增加 1 1 1
    • 取出一个非空柱子的砖块,花费 B B B,高度减小 1 1 1.
    • 移动一个柱子的一个砖块到另一个柱子上,移动柱子高度减 1 1 1,目标柱子高度增 1 1 1,花费为 M M M

    问将所有柱子变成相同高度的最小花费。

  • 解题思路

    我们知道假设最优解的高度为 x x x,那么减小 x x x会增加花费,增加 x x x也同样会增加花费,这个实际上就是一个开口向上的函数。普通的二分高度只能针对单调的函数,所以这里就要采用专门针对这种类型的三分求最优解了,具体思想为:

    控制左右端点形成的区间 [ l , r ] [l,r] [l,r],枚举 l m i d lmid lmid r m i d rmid rmid l m i d lmid lmid代表第一个分割点, r m i d rmid rmid代表第二个分割点,那么我们比较这两个点哪个高度更低(即花费更少),那么花费低的一定更靠近最优解,所以我们修改区间变成 [ l m i d + 1 , r m i d ] [lmid + 1,rmid] [lmid+1,rmid]或者 [ l m i d , r m i d − 1 ] [lmid,rmid-1] [lmid,rmid1]。直到区间长度为 1 1 1即可。跟二分思想差不多,如果是开口向下的,同理。

    需要注意第三个操作就等同于第一个操作加第二个操作,所以 M = m i n ( M , A + B ) M=min(M,A+B) M=min(M,A+B)

  • AC代码

/**
  *@filename:E
  *@author: pursuit
  *@created: 2021-09-01 12:01
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl

using namespace std;

typedef pair<int,int> pii;
typedef long long ll;
const int N = 1e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;

//修复后所有主子的高度相同。
//放砖成本为a,取出砖的成本为r。移动的成本为m。
//m = m(m, a + r);
int n, a, r, m, h[N];
ll check(int x){
    //砖块所有的高度为x。
    ll sum1 = 0, sum2 = 0;
    for(int i = 1; i <= n; ++ i){
        if(h[i] < x)sum1 += x - h[i];//放砖
        else sum2 += h[i] - x;//取砖。
    }
    ll cnt = min(sum1,sum2);
    sum1 -= cnt, sum2 -= cnt;
    return sum1 * a + sum2 * r + cnt * m;
}
void solve(){
    m = min(m, a + r);
    int l = 0, r = 1e9;
    while(l < r){
        int lmid = l + (r - l) / 3, rmid = r - (r - l) / 3;
        if(check(lmid) < check(rmid)){
            r = rmid - 1;
        }
        else{
            l = lmid + 1;
        }
    }
    printf("%lld\n", check(l));
}
int main(){	
    scanf("%d%d%d%d", &n, &a, &r, &m);
    for(int i = 1; i <= n; ++ i){
        scanf("%d", &h[i]);
    }
    solve();
    return 0;
}
posted @   unique_pursuit  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
点击右上角即可分享
微信分享提示