其他分享
首页 > 其他分享> > Codeforces Round #701 (Div. 2) ABC

Codeforces Round #701 (Div. 2) ABC

作者:互联网

Codeforces Round #701 (Div. 2) ABC

A - Add and Divide

题意

给定两个整数 a , b a, b a,b ,你可以进行两种操作:

  1. a = ⌊ a b ⌋ a = \lfloor \frac{a}{b} \rfloor a=⌊ba​⌋ (用 ⌊ a b ⌋ \lfloor \frac{a}{b} \rfloor ⌊ba​⌋ 替换 a a a);
  2. b = b + 1 b = b + 1 b=b+1(使 b b b 加1)

找出能使 a = 0 a = 0 a=0 的最小操作次数。

思路

首先,操作 2 2 2 一定是先于操作 1 1 1 进行的,因为操作 1 1 1 是指数级别减少,而 2 2 2 是线性减少, 2 2 2 放在 1 1 1 之前就能使 2 2 2 的影响从线性变为指数级别;

容易看出,若设总操作次数为 y y y,其中操作 2 2 2 的次数为 x x x ,则 y y y 关于 x x x 的函数在取值范围内先减后增(我不太能证明出来,但确实是这个规律)。所以我们直接从 x = 0 x = 0 x=0 开始遍历尝试,求出每次的操作数,找其最小值,找到函数拐点后推出循环即可。

有一个很坑的地方是,不能用C++的log 函数,当然也可能是我用错方式了。第一次写的时候我的代码是 ll ans = log(a) / log(b) + i + 1; ,这一步在有一个数据点上一直出错,表现为 log(a) / log(b) + i + 1 的值没有问题,但是赋值给 ans 就变了,非常奇怪,后来直接换成遍历求log就过了。

代码

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define PI acos(-1)
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 1e4 + 19;
const ll mod = 1e9 + 7;

int calc(int a, int b)
{
    int ans = 0;
    while(a >= b)
    {
        a /= b;
        ans++;
    }
    return ans;
}

int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        int a, b;
        cin >> a >> b;
        if(a < b)
        {
            cout << 1 << endl;
            continue;
        }
        int ans = INF;
        for(int i = 0; b + i <= a; i++)
        {
            if(b + i == 1)
                continue;
            int tmp = i + 1 + calc(a, b + i);
            if(tmp <= ans)
            {
                ans = tmp;
            }
            else
            {
                break;
            }
        }
        ans = min(ans, a - b + 2);
        cout << ans << endl;
    }
    return 0;
}

B - Replace and Keep Sorted

题意

两个数组被称为 k k k 相似,当且仅当:

  1. 都严格递增;
  2. 长度相等;
  3. 所有元素都是 1 1 1~ k k k 之间的正整数(包含边界);
  4. 他们只在一个位置值不同。

给定 k k k 和一个严格递增且每个元素值都不超过 k k k 、不小于 1 1 1 的数组 a a a ,询问 q q q 次,每次给定两个数 l , r l, r l,r ,要求你回答对于 [ l , r ] [l,r] [l,r] 范围内的 a a a 的子数组(即数组 [ a l , a l + 1 , … , a r ] [a_l, a_{l + 1}, …, a_r] [al​,al+1​,…,ar​]),有多少个数组和这个数组 k k k 相似。

思路

乍一看很复杂,细想很简单的题。

因为只能有一个位置值不同,所以我们先枚举可能出现不同的位置,那么显然是从 l l l 到 r r r 的每个位置都可以是不同的位置。

那这样的话,问题就变成了求每个点可以变化的值的个数,我们只需要预处理出每个点可以变化的范围即可,这个范围显然是 [ m a x ( a i − 1 + 1 , 1 ) , m i n ( a i + 1 − 1 , k ) ] − 1 [max(a_{i - 1} + 1, 1),min(a_{i + 1} - 1, k)] - 1 [max(ai−1​+1,1),min(ai+1​−1,k)]−1 ,即以上一个数为底,下一个数为顶,其中自己这个数不能取(不然就不变了)。

最后,这个数据范围并不允许我们每次从 l l l 到 r r r 遍历求和,所以也预先求出前缀和数组,查询时直接相减即可。

这题的边界我是单独拎出来处理的,即1个数、2个数、3个数的情况单独处理,没有想到很好的办法就if/else了。

代码

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define PI acos(-1)
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 1e6 + 19;
const ll mod = 1e9 + 7;

int a[N];
int dif[N];
int num[N];

int main()
{
    int n, q, k;
    cin >> n >> q >> k;
    cin >> a[0];
    a[n] = k;
    dif[0] = a[0] - 1;
    for(int i = 1; i < n; i++)
    {
        cin >> a[i];
        dif[i] = a[i] - a[i - 1] - 1;
        dif[i - 1] += a[i] - a[i - 1] - 1;
        if(i == n - 1)
            dif[i] += max(k - a[i] - 1, 0);
    }
    num[0] = dif[0];
    for(int i = 1; i < n; i++)
    {
        num[i] = num[i - 1] + dif[i];
    }
    for(int i = 0; i < q; i++)
    {
        int l, r;
        cin >> l >> r;
        l--;
        r--;
        if(l == r)
        {
            cout << k - 1 << endl;
        }
        else if(r - l == 1)
        {
            cout << a[r] - 2 + (k - a[l] - 1) << endl;
        }
        else if(r - l == 2)
        {
            cout << a[l + 1] - 2 + (k - a[r - 1] - 1) + dif[l + 1] << endl;
        }
        else
        {
            cout << a[l + 1] - 2 + (k - a[r - 1] - 1) + num[r - 1] - num[l] << endl;
        }
    }
    return 0;
}

C - Floor and Mod

题意

对给定的 x , y x, y x,y ,找到所有的 a ∈ [ 1 , x ] ,   b ∈ [ 1 , y ] a \in [1, x], \ b \in [1, y] a∈[1,x], b∈[1,y] ,满足 ⌊ a b ⌋ = a m o d    b \lfloor \frac{a}{b} \rfloor = a \mod b ⌊ba​⌋=amodb ,输出这样的数对一共有几对。

思路

问题其实就是:

  1. 先求出对于 b i ∈ [ 2 , y ] b_i \in [2, y] bi​∈[2,y] ( 1 1 1 肯定不行),有 c i c_i ci​ 个数,可使 a = ( b + 1 ) ∗ k    ( k ∈ [ 1 , b − 1 ] ) a = (b + 1) * k \ \ (k \in [1, b - 1]) a=(b+1)∗k  (k∈[1,b−1]) 成立。所有能使原式成立的 k k k 都满足:
    1 ≤ k ≤ b − 1 ,    1 b + 1 ≤ k ≤ x b + 1 1 \leq k \leq b - 1 ,\ \ \frac{1}{b + 1} \leq k \leq \frac{x}{b + 1} 1≤k≤b−1,  b+11​≤k≤b+1x​
    因此,不难得出:
    c i = m i n ( b − 1 , x b + 1 ) c_i = min(b - 1, \frac{x}{b + 1}) ci​=min(b−1,b+1x​)

  2. a n s = ∑ i = 2 y c i ans = \sum_{i = 2}^{y} c_i ans=∑i=2y​ci​。

但是第一步直接这样硬算是会超时的,所以我们对 c i c_i ci​ 究竟会是 b − 1 b- 1 b−1 还是 x b + 1 \frac{x}{b + 1} b+1x​ 进行分类讨论,这步的计算过程略,结果如下:
c i = { b − 1 ,       1 ≤ c i ≤ x + 1 这部分直接等差数列求和 x b + 1 ,       x + 1 ≤ c i ≤ x 这部分整除分块求和 c_i=\begin{cases} b - 1, \ \ \ \ \ 1 \leq c_i \leq \sqrt{x + 1}&\text{这部分直接等差数列求和}\\\frac{x}{b + 1}, \ \ \ \ \ \sqrt{x + 1} \leq c_i \leq x &\text{这部分整除分块求和}\end{cases} ci​={b−1,     1≤ci​≤x+1 ​b+1x​,     x+1 ​≤ci​≤x​这部分直接等差数列求和这部分整除分块求和​
第二部分的整除分块,即对于一定区间内的 b b b , x b + 1 \frac{x}{b + 1} b+1x​ 取整后的值是相同的。所以这部分放在一起计算,节省时间,少了这步会超时。

我觉得有必要把整除分块的复习提上日程。

代码

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define PI acos(-1)
#define please return
#define AC 0
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 1e6 + 19;
const ll mod = 1e9 + 7;

ll calc(int x, int y)
{
    int sq = min((int)sqrt(x + 1), y);
    ll ans = sq * (sq - 1) / 2;
    for(int b = sq + 1; b <= y && x / (b + 1) != 0;)
    {
        int tmp = x / (b + 1);
        int nxtb = min(x / tmp, y + 1);
        ans += (nxtb - b) * tmp;
        b = nxtb;
    }
    return ans;
}

int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        printf("%lld\n", calc(x, y));
    }
    please AC;
}

总结

以后double转int的精度问题真的需要好好思考再下手,这个A题确实不明不白卡了很久才发现是log函数的问题(也要做好复杂度估算)。

多多包涵,共同进步

标签:ABC,frac,int,ll,701,leq,ans,Div,define
来源: https://blog.csdn.net/qq_49451105/article/details/113811002