码迷,mamicode.com
首页 > 其他好文 > 详细

CF Round 701 Div2 解题补题报告

时间:2021-02-16 12:41:07      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:依次   操作   正整数   连续   元素   情况下   相同   int   alc   

A题 Add and Divide (贪心,枚举)

给定两个数字 \(a\)\(b\) 。现在你可以进行两个操作:

  1. \(a=?\frac{a}{b}?\)
  2. 使 \(b\) 的值加一

问使得 \(a=0\) 的最短操作次数是多少?

\(1\leq a,b \leq 10^9\)

有两个比较显然的结论:

  1. 显然,\(b=1\) 的时候必须加一下,不然永远也除不完。
  2. 根据贪心原则,第二类操作最好尽量先做完,这样不会使得答案更差

好了,比较显然的结论就到这了,下面怎么办?枚举?二分答案?爆搜?

提到二分,虽然实际上我们可能确实用不到这样复杂的算法(这才是 Div2 A),但是,二分咋感觉和操作 \(1\) 莫名有点关联?

注意到,当\(b=2\) 时,即使不进行操作 \(2\),只需要执行 \(\log_{2}^{a}\) 次左右的操作,便可以将其变为 \(0\) 。而这个操作的最大范围也就是三四十左右,显然可以枚举。

简单说,就是枚举操作 \(2\) 的次数(最多三四十次),然后依次判断,取最优答案即可,复杂度 \(O(\log^2 n)\)

#include <bits/stdc++.h>
using namespace std;
int calc(long long a, long long b) {
    int ans = 0;
    while (a) a /= b, ++ans;
    return ans;
}
void solve()
{
    long long a, b;
    cin>>a>>b;
    int ans = 1000000;
    for (int i = (b == 1) ? 1 : 0; i <= 100; ++i)
        ans = min(ans, calc(a, b + i) + i);
    cout<<ans<<endl;
}
int main()
{
    int T;
    cin>>T;
    while (T--) solve();
    return 0;
}

B题 Replace and Keep Sorted (数学,前缀和)

如果两个数列满足以下性质,那么称其为 \(k-similar\) 的:

  1. 两个数列都是严格单调增的
  2. 两个数列的值都是正整数,在区间 \([1,k]\)
  3. 两个数列元素个数相同
  4. 两个数列,有且只有一个位置的数不一样

给定一个数列 \(\{a_n\}\) ,现在要回答 \(q\) 次询问:每次询问给定一个区间 \([l,r]\),问能够构造出多少数列,和该区间提取出来的子数列是 \(k-similar\) 的?

\(1\leq n,q \leq 10^5,n \leq k \leq 10^9\),保证数列 $ {a_n}$ 本身符合该性质。

对于一个数列 \(\{c_n\}\),想要构造一个符合要求的数,那么显然就是枚举每一位,看看这一位能够选那些数,保证严格单调性质,对于第一位和最后一位特殊处理即可。

但是朴素的解法的复杂度是 \(O(qn)\) ,会超时,所以我们必须将处理每次询问的复杂度从 \(O(n)\) 降到 \(O(1)\)

怎么降?这种显然连续段相加的东西,用前缀和维护是再好不过的了,这样就可以将总复杂度降到 \(O(n+q)\) 了,可以轻松 \(AC\)。注意,这题小细节有点多,写起来要仔细。

#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n, q, k, a[N];
int t[N];
long long sum[N];
int main()
{
    scanf("%d%d%d", &n, &q, &k);
    for (int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    a[0] = 0, a[n + 1] = k + 1;
    for (int i = 1; i <= n; ++i) {
        t[i] = (a[i+1] - 1) - (a[i-1] + 1);
        sum[i] = sum[i - 1] + t[i];
    }
    for (int i = 1; i <= q; ++i) {
        int l, r;
        scanf("%d%d", &l, &r);
        long long ans = sum[r] - sum[l-1] - (t[r] + t[l])
                    + (a[l+1] - 1) - 1 + k - (a[r-1] + 1);
        printf("%lld\n", ans);
    }
    return 0;
}

C题 Floor and Mod (数学)

记一组数对 \((a,b)\)完美的,当且仅当 \(?\frac{a}{b}?=a\%b\)

给定两个数 \(x,y\) ,问有多少组数对 \((a,b)\) ,满足 \(1\leq a \leq x,1 \leq b \leq y\) ,且这个数对是完美的

\(1 \leq x,y \leq 10^9\)

最朴素的写法当然是 \(O(xy)\) 的暴力,但显然会 \(T\)

尝试从数学角度分析,如果 \(b\) 确定,假设除数和模数都是 \(c\),那么显然 \(1\leq c <b\) (如果 \(c=0\),那显然 \(a=0\),不合题意)。换言之,我们就可以构造出一系列匹配的 \(a\) (即 \(bc+c=c(b+1)\)) :\(b+1,2(b+1),3(b+1),......,(b-1)(b+1)\)

在此基础上,我们就可以省一维,只枚举 \(b\) 即可,对应的 \(a\) 的数量可以由数学规律简单推出,代码如下:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
void solve()
{
    int x, y;
    cin>>x>>y;
    LL ans = 0;
    for (int b = 2; b <= y; ++b) {
        int tmp = x / (b + 1);
        ans += (tmp < b) ? tmp : b - 1;
    }
    cout<<ans<<endl;
}
int main()
{
    int T;
    cin>>T;
    while (T--) solve();
    return 0;
}

可惜的是,这个复杂度还是有点高,并不能卡过去(这个常数,剪枝啥的已经快压到极限了)。

枚举 \(a\) 根本不可行,枚举 \(b\) (如上)似乎也压不进去,那好像只能枚举 \(c\) 了(逃

老实说,我昨天尝试过了 \(O(\sqrt{n})\) 级别的解法(部分枚举 \(c\)\(c\) 过大的情况下枚举 \(b\),最后拼在一起),但是小细节太多,没法一一处理。今天看了题解,打算整一手:

(并不)显然,\(c^2 \leq c(b+1) = a \leq x\) ,所以 \(c \leq \sqrt{x}\)

那么,如果我们尝试枚举 \(c\),每次 \(O(1)\) 的找出对应的 \(a,b\) 的数量,岂不是就可以过了?

这个枚举策略也很简单:对于 \(c\) ,它可以枚举出的 \(b\) 的上限是 \(y\) ,下限是 \(c+1\) ;枚举出来的 \(a\) 的下限是 \(c(c+1+1)=c(c+2)\),上限是 \(c(c+m)\)\(m=?\frac{a}{c}?-c\)

那么,对于每个 \(c\),统计两者的最小值,计入答案即可(记得边界要及时退出)

#include <bits/stdc++.h>
using namespace std;
void solve()
{
    int x, y;
    cin>>x>>y;
    long long ans = 0;
    //solve
    for(int c = 1; c+1 <= y && c*(c+2) <= x; ++c)
        ans += min(y, x/c - 1) - c;
    cout<<ans<<endl;
}
int main()
{
    int T;
    cin>>T;
    while (T--) solve();
    return 0;
}

CF Round 701 Div2 解题补题报告

标签:依次   操作   正整数   连续   元素   情况下   相同   int   alc   

原文地址:https://www.cnblogs.com/cyhforlight/p/14400069.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!