Codeforces Round #701 (Div. 2)赛后补题报告(A~D)

Codeforces Round #701 (Div. 2)赛后补题报告(A~D)

A. Add and Divide

原题信息

http://codeforces.com/contest/1485/problem/A
在这里插入图片描述

解题思路

对于题目基本有两种方式,一种是直接暴力求解,第二种是使用函数求导进行严格证明
暴力求解
a = 1 e 9 a=1e^9 a=1e9不难看出,操作最多为 50次,因为 2 4 9 = 562949953421312 2 ^ 49 = 562949953421312 249=562949953421312 直接就超了
那么我们的 b b b最多也就是加50次,然后进行向下整除的模拟即可
一定要注意 b = 0 b = 0 b=0这种情况。
函数求导
首先我们设操作次数为 y y y,进行 b + = 1 b += 1 b+=1的次数为 x x x,可以得到的关系式为
y = x + ( l o g b + x a + 1 ) y = x + (log_{b+x}a+1) y=x+(logb+xa+1),其中 l o g b + x a log_{b+x}a logb+xa是整除到1的次数,最后 +1是让其变为0
下面我们对他进行求导
y ′ = 1 − l n a ( l n t ) 2 ⋅ t y'=1-\frac{ln{a}}{(lnt)^2\cdot t} y=1(lnt)2tlna,其中 t = b + x , t ≥ 2 并 且 t ≥ b t=b+x,t\geq2 并且 t\geq b t=b+xt2tb
查看导函数的零点
( l n t ) 2 ⋅ t = l n a , t ≥ 2 并 且 t ≥ b {(lnt)^2\cdot t}=lna,t\geq2 并且 t\geq b (lnt)2t=lnat2tb,等式左侧递增,右侧为常数 l n a lna lna,即至多有一个零点
倘若有一个零点
可以直接二分求得零点,我们在零点,零点+1, 零点-1进行求解得结果。
倘若没有零点
直接就是 t ≥ 2 并 且 t ≥ b t\geq2 并且 t\geq b t2tb取得最小值。

AC代码

首先必须吐嘲一下,暴力没往这个向限制,求导生疏了,整了半天,真鸡儿狗
暴力枚举代码

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N = 100010;

LL a, b;

int sol(LL a, LL b, int add)
{
    b += add;
    if (b == 1) return 0x3f3f3f3f;
    while (a)
    {
        a /= b;
        add ++;
    }
    return add;
}

int main()
{
    int T;  cin >> T;
    while (T -- )
    {
        scanf("%lld%lld", &a, &b);
        int res = 1000;
        for (int i = 0; i <= 50; i ++ )
        {
            res = min(res, sol(a, b, i));
        }
        cout << res << endl;
    }
    return 0;
}

函数求导代码

#include <bits/stdc++.h>
using namespace std;

const int N = 100010;
typedef long long LL;
const double esp = 1e-5;
LL a, b;


int RealDo(LL a, LL b)
{
    int ret = 0;
    while (a)
    {
        ret ++;
        a /= b;
    }
    return ret;
}

int Get(LL a, LL b, LL tarb)
{

    if (tarb <= 1 || tarb < b)  return 0x3f3f3f3f;
    // return (tarb - b) + floor(log(a) / log(tarb)) + 1;
    return (tarb - b) + RealDo(a, tarb);
}


double Cal(LL a, LL b)
{
    double lga = log(a);
    double l = b, r = 2e9, mid = b;
    if (lga <= mid * (log(mid) * log(mid)) )    // 不用加
        return mid;

    while (abs(l - r) >= esp)
    {
        mid = (l + r) / 2;
        if (lga <= mid * (log(mid) * log(mid))) // 高了
            r = mid;
        else    // 低了
            l = mid;
    }
    return mid;
}

int sol(LL a, LL b)
{
    double x = Cal(a, b);

    return min(Get(a, b, x), min(Get(a, b, x + 1), Get(a, b, x - 1)));
}

int main()
{
    int t;
    cin >> t;
    while (t -- )
    {
        scanf("%lld%lld", &a, &b);
        cout << sol(a, b) << endl;
    }

    return 0;
}

B. Replace and Keep Sorted

原题信息

http://codeforces.com/contest/1485/problem/B
在这里插入图片描述

解题思路

我们单独考虑端点的可能性,已经中间结点不同的次数(可以使用前缀和)
但是一定要仔细考虑什么时候 + 1,什么时候没有+1, 端点是否包括

AC代码

#include <bits/stdc++.h>
using namespace std;

const int N = 100010;
int n, k, q;
int a[N];
int b[N];

int cal(int l, int r)
{
    if (l == r) return  k - 1;
    else
    {
        int ret = (a[l] - 1) + (a[l + 1] - a[l] - 1) + (k - a[r]) + (a[r] - a[r - 1] - 1);
        if (l == r - 1)
            return ret;
        else
            return ret + b[r - 1] - b[l];   // l + 1 ~ r - 1
    }
}
int main()
{
    cin >> n >> q >> k;
    for (int i = 1; i <= n; i ++ )
        scanf("%d", &a[i]);

    memset(b, 0, sizeof b);
    for (int i = 2; i <= n - 1; i ++ )
        b[i] = (a[i] - a[i - 1] - 1) + (a[i + 1] - a[i] - 1);

    for (int i = 2; i <= n - 1; i ++ )
        b[i] += b[i - 1];

    while (q -- )
    {
        static int l, r;
        scanf("%d%d", &l, &r);
        // cout << "res" << endl << "\t";
        cout << cal(l, r) << endl;
    }
    return 0;
}

C. Floor and Mod

原题信息

http://codeforces.com/contest/1485/problem/C
在这里插入图片描述

解题思路

C题吐槽一下,当时复杂度分析错了, O ( N ) O(\sqrt N) O(N )分析成 O ( N ) O(N) O(N),太绝了
⌊ a b ⌋ = a % b = t , t < b \lfloor\frac a b\rfloor=a\%b=t, t< b ba=a%b=t,t<b
那么 a = t ⋅ b + t = ( b + 1 ) ⋅ t , A ≥ a ≥ 1 , B ≥ b ≥ 1 a=t \cdot b + t=(b+1)\cdot t, A\geq a\geq1,B\geq b\geq1 a=tb+t=(b+1)t,Aa1,Bb1

  • t = 1 t=1 t=1时, a = b + 1 , b ∈ [ 2 , B ] a=b+1,b\in [2, B] a=b+1,b[2,B]
  • t = 2 t=2 t=2时, a = 2 ⋅ ( b + 1 ) , b ∈ [ 3 , B ] a=2\cdot(b+1),b\in [3, B] a=2(b+1),b[3,B]
  • t = i t=i t=i时, a = i ⋅ ( b + 1 ) , b ∈ [ i + 1 , B ] a=i\cdot(b+1),b\in [i + 1, B] a=i(b+1),b[i+1,B]
    此时, i ⋅ ( b + 1 ) ≤ A , b ≤ B , b ≥ i + 1 i \cdot (b + 1)\leq A, b\leq B, b\geq i+1 i(b+1)A,bB,bi+1
    ⟹ b ∈ [ i + 1 , m i n ( B , ⌊ A i ⌋ − 1 ) ] \Longrightarrow b\in [i + 1, min(B,\lfloor \frac A i \rfloor-1)] b[i+1,min(B,iA1)]
    如果 ⌊ A i ⌋ − 1 \lfloor \frac A i \rfloor-1 iA1有前缀和公式的话,是可以进行化简为 O ( 1 ) O(1) O(1)
    最后, b ∈ [ i + 1 , m i n ( B , ⌊ A i ⌋ − 1 ) ] b\in [i + 1, min(B,\lfloor \frac A i \rfloor-1)] b[i+1,min(B,iA1)]可以看出来 i i i的最大值,是无法取到 1 e 9 1e9 1e9的,需要开个根号。

AC代码

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;


void Sol(LL A, LL B)
{
    LL a, b, tmp, ret = 0;
    for (int i = 1; true; i ++ )
    {
        tmp = min(B, A / i - 1);
        // i + 1 ~ tmp
        if (i + 1 > tmp)    break;
        else    ret += (tmp - (i + 1) + 1);
    }
    cout << ret << endl;
}

int main()
{
    int t;  cin >> t;
    while (t -- )
    {
        static LL a, b;
        scanf("%lld%lld", &a, &b);
        Sol(a, b);

    }

    return 0;
}

D. Multiples and Power Differences

原题信息

http://codeforces.com/contest/1485/problem/D
在这里插入图片描述

解题思路

可恶啊,这个题目是一个LCM的板子题,没看出来。。。
首先,我们对1~16求LCM = x,我们的 B 数组首先全是x,可以满足 m u l t i p l e multiple multiple的性质,然后我们凑性质三,发现可以直接进行隔项加上 a i , j 4 a_{i, j}^4 ai,j4,而且数据范围小,满足性质1

AC代码

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N = 510;
int n, m;
int a[N][N];
int b[N][N];

int gcd(int x, int y)
{
    if (y == 0) return x;
    else    return gcd(y, x % y);
}

int lcm(int x, int y)
{
    return LL(x) * y / gcd(x, y);
}

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            scanf("%d", &a[i][j]);

    int x = 1;
    for (int i = 1; i <= 16; i ++ )
    {
        x = lcm(x, i);
    }

    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            b[i][j] = x;

    for (int i = 1; i <= n; i ++ )
    {
        for (int j = (i % 2 == 1 ? 1 : 2); j <= m; j += 2)
        {
            b[i][j] += (a[i][j] * a[i][j]) * (a[i][j] * a[i][j]);
        }
    }

    // output
    for (int i = 1; i <= n; i ++ )
    {
        printf("%d", b[i][1]);
        for (int j = 2; j <= m; j ++ )
            printf(" %d", b[i][j]);
        puts("");
    }

    return 0;
}

总结

  • 首先考虑暴力,能否直接缩小范围,省时间
  • 其次,考虑复杂度的时候,分析准确一些
  • 多考虑边界的情况,+1,-1
  • 最后题型要把握清楚,数据范围小的时候,有时候会暴力,有时候直接lcm。。。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值