Codeforces Round #643 (Div. 2) 做题记录

博客详细解析了Codeforces Round #643 (Div. 2) 中的六道编程题目,包括题目的意思、数据范围和解题思路,涉及模拟、贪心、枚举、前缀和等多种算法。
摘要由CSDN通过智能技术生成

A:Sequence with Digits

题意:定义 a n + 1 = a n + m i n D i g i t ( a n ) ∗ m a x D i g i t ( a n ) a_{n + 1} = a_{n} + minDigit(a_{n}) * maxDigit(a_{n}) an+1=an+minDigit(an)maxDigit(an),给定 a 1 a_{1} a1 a k a_{k} ak
数据范围: 1 < = a 1 < = 1 0 18 , 1 < = k < = 1 0 16 1 <= a_{1} <= 10 ^ {18}, 1 <= k <= 10 ^ {16} 1<=a1<=1018,1<=k<=1016
题解:只要在中间步骤中某个 a i a_{i} ai存在数位0,就不会再增大了,发现很快就会出现数位0,直接模拟即可。
代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
ll a, k;
int main() {
    int t;
    scanf("%d", &t);
    while(t--) {
        scanf("%I64d%I64d", &a, &k);
        while(k > 1) {
            ll Min = 9, Max = 0;
            ll b = a;
            while(b > 0) {
                Min = min(Min, b % 10);
                Max = max(Max, b % 10);
                b /= 10;
            }
            if(Min == 0) break;
            a += Min * Max;
            --k;
        }
        printf("%I64d\n", a);
    }
    return 0;
}

B:Young Explorers

题意:有 n n n个人,每个人有一个权值 e i e_i ei,表示这个人所在的组的人数必须大于等于 e i e_i ei,对这些人进行分组,对于一个人可以选择不对其进行分组,问最多可以分多少组。
数据范围: 1 < = n < = 2 ∗ 1 0 5 , 1 < = e i < = n 1<=n<=2*10^{5},1<=e_i<=n 1<=n<=2105,1<=ei<=n
题解:用桶记录每个权值的人数,从小到大贪心分组,当小权值的人数不够再分一组时,将人数加到大权值中。
代码:

#include <bits/stdc++.h>

using namespace std;
const int maxn = 3e5 + 7;
int n, a[maxn], cnt[maxn];
int main() {
    int t;
    scanf("%d", &t);
    while(t--) {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i) cnt[i] = 0;
        for(int i = 1; i <= n; ++i) {
            scanf("%d", a + i);
            cnt[a[i]]++;
        }
        int ans = 0;
        for(int i = 1; i <= n; ++i){
            ans += cnt[i] / i;
            cnt[i + 1] += cnt[i] % i;
        }
        printf("%d\n", ans);
    }
    return 0;
}

C:Count Triangles

题意:给定4个整数 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<=x<=B<=y<=C<=z<=D A<=x<=B<=y<=C<=z<=D的三角形数量。
数据范围: 1 < = A , B , C , D < = 5 ∗ 1 0 5 1 <= A,B,C,D<=5*10^5 1<=A,B,C,D<=5105
题解:先枚举 x x x,由 z z z的上下限确定合法 y y y的范围,利用前缀和求出每个 y y y有多少种合法的三角形即可。
代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int maxn = 5e5 + 7;
ll sum[maxn];
int main() {
    int a, b, c, d;
    scanf("%d%d%d%d", &a, &b, &c, &d);
    ll ans = 0;
    for(int i = a; i <= b; ++i) {
        int Mi = c - i, Ma = d - i;
        ++sum[Mi + 1];
        --sum[Ma + 2];
    }
    for(int i = 1; i <= c; ++i) sum[i] += sum[i - 1];
    for(int i = 1; i <= c; ++i) sum[i] += sum[i - 1];
    for(int i = b; i <= c; ++i) ans += sum[i];
    printf("%I64d\n", ans);
    return 0;
}

D:Game With Array

题意:给两个整数 N N N S S S,构造出一个长度为 N N N的序列 a a a,其元素之和为 S S S,要求 a a a的所有子段和不能全部构成 0... S / 2 0...S / 2 0...S/2。可以构造输出YES,并输出构造的数组 a a a和不能构成的元素 K K K。无法构造输出NO。
数据范围: 1 < = N < = S < = 1 0 6 1<=N<=S<=10^6 1<=N<=S<=106
题解:当 S > = 2 ∗ N S>=2 *N S>=2N时,显然将 a a a中元素全部设成 > = 2 >=2 >=2即可,这样不能构成1。后面大力猜结论,当 S < 2 ∗ N S < 2*N S<2N时,无法构造出序列 a a a(其实是不想证QAQ)。
代码:

#include <bits/stdc++.h>

using namespace std;
const int maxn = 1e6 + 7;
int a[maxn];
int n, s;
int main() {
    scanf("%d%d", &n, &s);
    if(s >= 2 * n) {
        s -= 2 * n;
        puts("YES");
        printf("%d", 2 + s);
        for(int i = 1; i < n; ++i) printf(" %d", 2);
        puts("");
        puts("1");
    }
    else puts("NO");
    return 0;
}

E:Restorer Distance

题意:给一个长度为 n n n的序列 h h h,有3中操作:
1.选择一个位置 i i i,使 h i = h i + 1 h_i =h_i+1 hi=hi+1,代价为 A A A
2.选择一个位置 i ( h i > 0 ) i(h_i>0) i(hi>0),使 h i = h i − 1 h_i=h_i-1 hi=hi1,代价为 B B B
3.选择两个位置 i , j ( h i > 0 ) i,j(h_i>0) i,j(hi>0),使 h i = h i − 1 , h j = h j + 1 h_i=h_i-1,h_j=h_j+1 hi=hi1,hj=hj+1,代价为 M M M
求使序列 h h h中所有元素都相等的代价最下是多少。
数据范围: 1 < = n < = 1 0 5 , 1 < = A , R , M < = 1 0 4 , 1 < = h i < = 1 0 9 1<=n<=10^5,1<=A,R,M<=10^4,1<=h_i<=10^9 1<=n<=105,1<=A,R,M<=104,1<=hi<=109
题解:这看起来就是有极值的,直接三分最终的元素值即可。
代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int maxn = 1e5 + 7;
int h[maxn];
int n, A, R, M;

ll check(int x) {
    ll cnta = 0, cntb = 0, cntm;
    for(int i = 1; i <= n; ++i) {
        if(h[i] < x) cnta += x - h[i];
        else cntb += h[i] - x;
    }
    cntm = min(cnta, cntb);
    cnta -= cntm;
    cntb -= cntm;
    return cnta * A + cntb * R + cntm * M;
}
int main() {
    scanf("%d%d%d%d", &n, &A, &R, &M);
    for(int i = 1; i <= n; ++i) scanf("%d", h + i);
    M = min(M, A + R);
    int l = 0, r = 1e9 + 7, lmid, rmid;
    while(r - l > 2) {
        lmid = l + (r - l) / 3;
        rmid = r - (r - l) / 3;
        if(check(lmid) > check(rmid)) {
            l = lmid;
        }
        else r = rmid;
    }
    ll ans = 1e18;
    while(l <= r) ans = min(ans, check(l++));
    printf("%I64d\n", ans);
    return 0;
}

F:Guess Divisors Count

题意:交互题,给一个隐藏的 X ( 1 < = X < = 1 0 9 ) X(1<=X<=10^9) X(1<=X<=109),每次询问可以给出一个 Q ( 1 < = Q < = 1 0 18 ) Q(1<=Q<=10^{18}) Q(1<=Q<=1018),问 g c d ( X , Q ) gcd(X,Q) gcd(X,Q)是多少,最多进行22次询问,最后回答 X X X大概有多少个因子,误差允许的范围:设你的答案为 a n s ans ans X X X的因子数为 d d d,则需要满足下面两个要求之一则算作正确:
1. ∣ a n s − d ∣ < = 7 |ans - d| <= 7 ansd<=7
2. 1 2 < = a n s d < = 2 \frac 12<=\frac {ans}{d}<=2 21<=dans<=2
题解:首先,要知道可以通过询问 X X X中素因子次幂我们可以知道其因子数。因为 X X X的范围在 1 0 9 10^9 109,只要知道了 1000 1000 1000以内的 X X X的因子数就可以知道 X X X大约 2 3 \frac 23 32的因子数,以这个为切入点,将 1000 1000 1000以内的素因子求出,发现 1000 1000 1000内的素数有168个,将每个素因子都乘到它 1000 1000 1000范围内的最大幂次a即 p a < = 1000 p^a<=1000 pa<=1000用来求 X X X 1000 1000 1000内的素因子次幂,又因为询问的数 Q Q Q最大范围是 1 0 18 10^{18} 1018,所以可以通过将多个素因子乘在一起询问,减少询问的次数。最后处理完后发现还要26个询问,但题目只要求超过 1 2 \frac 12 21且相差不大于7,所以少询问几个大的素因子无伤大雅。
代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;


int query(ll x) {
    cout << "? " << x << endl;
    int res;
    cin >> res;
    return res;
}
void answer(int ans) {
    cout << "! " << ans << endl;
}
vector<ll> prime, p, q;
int vis[1100];
int main() {
    //168个
    for(int i = 2; i <= 1000; ++i) {
        if(vis[i]) continue;
        prime.push_back(i);
        for(int j = i; j <= 1000; j += i) vis[j] = 1;
    }
    int Max = 1024;
    for(ll &v : prime) {
        ll t = v;
        while(t <= Max) t *= v;
        t /= v;
        p.push_back(t);
    }
    ll qMax = 1e18;
    for(int i = 0; i < p.size();) {
        ll x = 1;
        while(i < p.size() && qMax / x >= p[i]) {
            x *= p[i++];
        }
        q.push_back(x);
    }
    //cout << q.size() << endl;
    int t;
    cin >> t;
    while(t--) {
        int ans = 1;
        for(int i = 0; i < 22; ++i) {
            int v = query(q[i]);
            for(ll &x : prime) {
                int c = 0;
                while(v % x == 0) ++c, v /= x;
                ans *= (c + 1);
            }
        }
        answer(ans * 2);
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值