Codeforces Round #643 (Div. 2)(A~F)

A. Sequence with Digits

思路

  • 题意
  1. 定义:minDigit(x)为x的各个位数中,值最小的那个值
  2. 定义:maxDigit(x)为各个位数中,值最大的那个值
  3. 给出一个递推式: a [ n + 1 ] = a [ n ] + m i n D i g i t [ n ] m i n D i g i t [ n ] a[n+1]=a[n]+minDigit[n]*minDigit[n]
  4. 在题目中 给我们了t次询问,对于每一次询问给出 a [ 1 ] a[1] 的值,让我求第 a [ k ] a[k] 的值
  • 分析:这题先看数据范围 n < = 1 e 5 , k < = 1 e 16 n<=1e5,k<=1e16 ,运行时间<1,这样的大的数据范围句不是暴力过的一定有规律,,我们考虑:一旦 a [ n ] a[n] 的某个十进制位上的数字等于0,那么 m i n D i g i t [ n ] m a x D i g i t [ n ] minDigit[n]*maxDigit[n] 的乘积就恒等于0,在之后表达式就变成了: a [ n + 1 ] = a [ n ] a[n+1]=a[n] 剩下的直接输出就行了

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<string>
void fre() { freopen("A.txt", "r", stdin); freopen("Ans.txt", "w", stdout); }
using namespace std;
#define ll long long 
const int INF = 0x3f3f3f3f;
const int mxn = 2e5 + 10;


int main()
{
    /* fre(); */
    int T;
    scanf("%d", &T);
    while(T --)
    {
        ll n, k;
        scanf("%lld %lld", &n, &k);
        k -= 1;
        while(k --)
        {
            string s = to_string(n);
            int mx = 0, mn = 10;
            for(auto x : s)
            {
                int dig = x - '0';
                mx = max(mx, dig);
                mn = min(mn, dig);
            }
            if(mn == 0) break;
            n = (n + mx * mn);
        }
         printf("%lld\n", n);
    }

    return 0;
}

B. Young Explorers

思路(贪心)

  • 题意
  1. 给我们n个数字,这些数字可以任意组合,形成一组,但是形成的这个组的要求是,这个组能内的 值最大的那个数字的值要小于等于组中数字的数量。这样任意组合最多可以形成几组?(某些数字,可以不必使用它来分组)。
  • 分析
  1. 一个简单的贪心,首先我们考虑要想分的组多,就要尽可能每组分的人少,而还要注意,没组分的人数多少是有值最大的那个人决定的,与有这个策略:我们每次从数字值小到大的顺序,枚举作为组中最大值,这个最大值就是组中需要的人数,例如这个最大值如果是2,那么这个组内要有两人,一个人的值为2,一个人的值要小于等于2(说的不清楚,直接看代码更简单)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<string>
void fre() { freopen("A.txt", "r", stdin); freopen("Ans.txt", "w", stdout); }
using namespace std;
#define ll long long 
const int INF = 0x3f3f3f3f;
const int mxn = 3e5 + 10;

ll ar[mxn];

int main()
{
    /* fre(); */
    int T;
    scanf("%d", &T);
    while(T --)
    {
        int n;
        scanf("%d", &n);
        memset(ar, 0, (sizeof(ll) + 1) * n );
        int t;
        for(int i = 1; i <= n; i ++)
        {
            scanf("%d", &t);
            ar[t] ++;
        }
        ll ans = 0;
        ll pre = 0;
        for(int i = 1; i <= n; i ++)
        {
            if(ar[i])
            {
                ar[i] += pre;
                ans += ar[i]/i;

                if(i + 1 <= n)
                    pre = ar[i] - ar[i] / i * i;
            }
        }
        printf("%lld\n", ans);
    }

    return 0;
}


C. Count Triangles

思路

  • 题意
  1. 给我们四个数 A,B,C,D,存在 A < = x < = B < = y < = C < = z < = D A<=x<=B<=y<=C<=z<=D ,问x,y,z能够造成的三角方案数是多少种?
  • 分析

我先分析一个,能够构成三角形的条件“两边两边之和大于第三边,两边之差小于第三边”,这题的两种方法的思路都是从 A B A~B 枚举第一条边x的取值,这样就相当于x的值是一个定值了,剩下的我们在枚举x的基础上对y属于 B C B~C 这个范围进行讨论,在讨论的时候我们要明白这个样一个恒成立的条件:$y-x<=z$,即:两边之差恒小于第三边,那我们只需考虑限制第三边的确定就只有一个限制条件:两边之和(x+y)小于第三边(z),下面两种思路都是 :都是对这个限制条件的作的相应解决办法。

  • 思路1
  1. 利用“差分”来解决区间所有元素加上一个相同的值,我们考虑x+y的最小值为a+b,最大值是b+c,我们可以差分计算出,这个区间内每个数有多少种可能的情况,再对数组计算前缀和,最后枚举z,看对于当前的z,有多少x+y>z的组合,输出答案即可。
  • 思路2(结合着思路1来理解)

在这里插入图片描述

代码1

#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <list>
#include <queue>
#include <deque>
#include <cmath>
#include <stack>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
#define endl '\n'
#define PI cos(-1)
#define PB push_back
#define ll long long
#define int long long 
#define INF 0x3f3f3f3f
#define mod 998244353
#define lowbit(abcd) (abcd & (-abcd))
#define fre                              \
    {                                    \
        freopen("A.txt", "r", stdin);   \
        freopen("Ans.txt", "w", stdout); \
    }
const int mxn = 1e6 + 10;
int f[mxn];

signed main()
{
    /* fre */
    ll a, b, c, d;
    scanf("%lld %lld %lld %lld", &a, &b, &c , &d);
    for(int i = a; i <= b; i ++)
    {
        f[i + b - 1] ++;
        f[i + c - 1 + 1] --;
    }

    for(int i = 1; i < mxn; i ++)       //求每个位置差分之后的数值
        f[i] += f[i - 1];
    for(int i = 1; i < mxn; i ++)       //求每个位置数值 前缀和
        f[i] += f[i - 1];
    ll ans = 0;
    for(int i = c; i <= d; i ++)
        ans += f[mxn - 1] - f[i - 1];
    printf("%lld\n", ans);  
}

代码2

#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <list>
#include <queue>
#include <deque>
#include <cmath>
#include <stack>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
#define endl '\n'
#define PI cos(-1)
#define PB push_back
#define ll long long
#define int long long 
#define INF 0x3f3f3f3f
#define mod 998244353
#define lowbit(abcd) (abcd & (-abcd))
#define fre                              \
    {                                    \
        freopen("A.txt", "r", stdin);   \
        freopen("Ans.txt", "w", stdout); \
    }

signed main()
{
    /* fre */
    ll a, b, c, d;
    scanf("%lld %lld %lld %lld", &a, &b, &c , &d);
    int ans = 0;
    for(int i = a; i <= b; i ++)    
    {
        int l = i + b - 1;
        int r = i + c - 1;      // l <= y <= r 的时候在每个对应的可能的y值为方案数为一个从1开始的公差为1的等差序列:[1, 2, ..., r-l+1]

        int s = 0, e = 0, ss = 0, ee = 0;
        if(l >= c) s  = l - c + 1;
        if(r >= c) e  = r - c + 1;        //[s, e] 之间的数表示答案数(但是有的答案可能超过了又边界,最后要被减去的
        if(l >  d) ss = l - d;                           //[ss, ee] 越过d边界的无用边
        if(r >  d) ee = r - d;
        ans += (e - s + 1) * (e + s) / 2;               //等差数列求和公式
        ans -= (ee - ss + 1) * (ee + ss) / 2;
    }
    printf("%lld\n", ans);
}

D. Game With Array

思路

  • 题意
  1. 让我们构造一个长度为你n的序列ar,并且序列的和为S,对于这个徐磊ar我们需要找出一个k( 1 < = k < = S 1<=k<=S ),是ar中任意子串和不等于k或者S-k。
  • 分析
  1. 第一个中思路就是:我们令ar中前n-1个数的值为1,第n个数的值为 S ( n 1 ) S-(n-1) , 这样如果 n 1 < ( S ( n 1 ) ) n-1<(S-(n-1)) 的话,这样我们就可以选择 ( n 1 , ( S ( n 1 ) ) (n-1,(S-(n-1)) 之间的数作为k,就是yes
  2. 第二种思路就是:我们令k=1,然后ar中的元素不要出现k或者S-k就行了,只要 S / n > = 2 S/n>=2 ,也即是(n-1)个2和一个 S ( n 1 ) 2 S-(n-1)*2 ,这样能构造出来就是yes。

代码(思路1)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<string>
void fre() { freopen("A.txt", "r", stdin); freopen("Ans.txt", "w", stdout); }
using namespace std;
#define ll long long 
const int INF = 0x3f3f3f3f;
const int mxn = 3e5 + 10;

ll ar[mxn];

int main()
{
    /* fre(); */
    int n, s;
    scanf("%d %d", &n, &s);
    int ct1 = n - 1;
    int cha = s - (n - 1);
    if(ct1 + 1 < cha)
    {
        printf("YES\n");
        for(int i = 1; i <= ct1; i ++)
            printf("1 ");
        printf("%d\n", cha);
        printf("%d\n", ct1 + 1);
    }
    else
        printf("NO\n");

    return 0;
}


E. Restorer Distance(三分枚举答案)

思路

  • 题意
  1. 给我们一个序列ar,我们可以进行三种操作: 1. a r [ i ] ,      2. a r [ i ] + + ,       3. ( a r [ x ] , a r [ y ] + + ( x ! = y ) 1. ar[i] --,~~~~2. ar[i]++,~~~~~3.(ar[x]--,ar[y]++(x!=y) 这三种操作的对应的话费为a,b,c
  2. 通过使用上面的三种操作,是ar中的元素的值变得相同,需要的最小花费是多少?
  • 分析
  1. 用二分枚举答案,但是这题的最小花费曲线是一个抛物线形状,所以该用三分枚举ar中所有的元素都要的变成的“x”,对于第三个操作 我们令 c = m i n ( c , a + b ) c=min(c,a+b) 的意思是如果第三种操作的费用比a+b的值的大的话,我们用第一和第二种操作来代替第三种操作,剩下的就是写一个judge()函数去判断白所有的ar都变成某个数x所需要的费用为judge(x)。

代码

#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <list>
#include <queue>
#include <deque>
#include <cmath>
#include <stack>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
#define endl '\n'
#define PI cos(-1)
#define PB push_back
#define ll long long
#define int long long 
#define INF 0x3f3f3f3f
#define mod 998244353
#define lowbit(abcd) (abcd & (-abcd))
#define fre                              \
    {                                    \
        freopen("A.txt", "r", stdin);   \
        freopen("Ans.txt", "w", stdout); \
    }
const int mxn = 1e5 + 10;
int ar[mxn];
int n, a, b, c;

int judge(int level)
{
    int sa = 0, sb = 0;         //两种操作分别的总费用
    for(int i = 1; i <= n; i ++)
    {
        if(ar[i] < level)
            sa += level - ar[i];
        if(ar[i] > level)
            sb += ar[i] - level;
    }
    int sc = min(sa, sb);
    sa -= sc; sb -= sc;
    return sc * c + sa * a + sb *  b;
}

signed main()
{
    /* fre; */
    scanf("%lld %lld %lld %lld", &n, &a, &b, &c);
    for(int i = 1; i <= n; i ++)
        scanf("%lld", &ar[i]);
    c = min(c, a + b);
    int l = 1, r = INF;
    while(l < r)
    {
        int lmid = l + (r - l) / 3;
        int rmid = r - (r - l) / 3;
        if(judge(lmid) < judge(rmid))
            r = rmid - 1;
        else
            l = lmid + 1;
    }
    printf("%lld\n", judge(l));
}

猜你喜欢

转载自blog.csdn.net/qq_34261446/article/details/106199875