当前位置: 首页 > news >正文

Codeforces Round 1017 (Div. 4)题解

题目地址

https://codeforces.com/contest/2094

锐评

本次题目没什么好吐槽的。题意难度都挺好的,也没什么坑,比较符合D4预期。奈何自己太菜,想的有点慢,时间不够。F题构造题是我的弱项,想到个弱解被叉了。

题解

Problem A. Trippi Troppi

题目大意

Trippi Troppi 生活在一个奇异的世界。每个国家的古代名称由三个字符串组成。每个字符串的第一个字母连起来就是该国的现代名称。

给定国家的古代名称,请输出现代名称。

题解思路:模拟

直接按照题意输出即可。时间复杂度为 O ( 1 ) O(1) O(1)

参考代码(C++)

#include <bits/stdc++.h>
using namespace std;
string sa, sb, sc;

void solve() {
    cin >> sa >> sb >> sc;
    cout << sa[0] << sb[0] << sc[0] << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t = 1;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

Problem B. Bobritto Bandito

题目大意

在波布里托-班迪托居住的小镇上,在一条无穷数线上有无穷多的房子,房子的位置是 … , − 2 , − 1 , 0 , 1 , 2 , … \ldots, -2, -1, 0, 1, 2, \ldots ,2,1,0,1,2, 。在 0 0 0 天,他让 0 0 0 号房子里不幸的居民感染了瘟疫。接下来的每一天,瘟疫都会传播到恰好1个健康的家庭,而这些家庭恰好与一个受感染的家庭相邻。可以看出,每天受感染的房屋形成一个连续的片段。

假设从第 l l l 户开始到第 r r r 户结束的线段表示为 [ l , r ] [l, r] [l,r] 。你知道在 n n n 天后, [ l , r ] [l, r] [l,r] 段被感染了。请找出可能在第 m m m 天( m ≤ n m \leq n mn )被感染的线段 [ l ′ , r ′ ] [l', r'] [l,r]

题解思路:数学

直接判断相差了几天,左/右半段缩短即可。但是要注意, 0 0 0 是一定要包含的。时间复杂度为 O ( 1 ) O(1) O(1)

参考代码(C++)

#include <bits/stdc++.h>
using namespace std;
int n, m, l, r;

void solve() {
    cin >> n >> m >> l >> r;
    int cnt = n - m;
    int ct = min(r, cnt);
    r -= ct;
    cnt -= ct;
    l += cnt;
    cout << l << ' ' << r << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t = 1;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

Problem C. Brr Brrr Patapim

题目大意

Brr Brrr 帕塔皮姆正在努力学习提拉米苏的秘密密码,它是 2 ⋅ n 2 \cdot n 2n 个元素的排列组合。为了帮助帕塔皮姆猜测,提拉米苏给了他一个 n × n n \times n n×n 网格 G G G ,其中 G i , j G_{i,j} Gi,j (或者说网格中第 i i i 行第 j j j 列中的元素)包含 p i + j p_{i+j} pi+j ,或者说排列组合中的第 ( i + j ) (i+j) (i+j) 个元素。

给定这个网格,请帮助帕塔皮姆破解被遗忘的密码。可以保证这个排列是存在的,而且可以证明这个排列是唯一确定的。

m m m 个整数的排列是 m m m 个整数的序列,其中都恰好包含 1 , 2 , … , m 1, 2, \ldots, m 1,2,,m 一次。例如, [ 1 , 3 , 2 ] [1, 3, 2] [1,3,2] [ 2 , 1 ] [2, 1] [2,1] 是排列,而 [ 1 , 2 , 4 ] [1, 2, 4] [1,2,4] [ 1 , 3 , 2 , 3 ] [1, 3, 2, 3] [1,3,2,3] 不是排列。

题解思路:模拟/构造

按照题意直接模拟构造,没构造出来的位置,补充即可。时间复杂度为 O ( n 2 l o g n ) O(n^2logn) O(n2logn) (因为我用了set,其实不用,序列是唯一的,所以可降为 O ( n 2 ) O(n^2) O(n2) )。

PS:其实只需要按照从左上角往右走7字型,然后从第2个位置开始填充即可,第1个位置直接可以算出来(详见第二份代码)。

参考代码(C++)

复杂度为 O ( n 2 l o g n ) O(n^2logn) O(n2logn) 版本代码。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1'605;
int a[maxn][maxn], b[maxn];
int n;

void solve() {
    cin >> n;
    int m = n << 1;
    set<int> st;
    for (int i = 1; i <= m; ++i) {
        st.insert(i);
        b[i] = -1;
    }
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= n; ++j) {
            cin >> a[i][j];
            b[i + j] = a[i][j];
            st.erase(b[i + j]);
        }
    for (int i = 1; i <= m; ++i)
        if (b[i] == -1) {
            b[i] = *st.begin();
            st.erase(st.begin());
        }
    for (int i = 1; i <= m; ++i)
        cout << b[i] << (" \n"[i == m]);
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t = 1;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

复杂度为 O ( n 2 ) O(n^2) O(n2) 版本代码。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1'605;
int a[maxn];
int n;

void solve() {
    cin >> n;
    int m = n << 1, id = 1, sum = 0, x;
    for (int i = 0; i < n; ++i)
        for (int j = 0; j < n; ++j) {
            cin >> x;
            if (i == 0 || j == n - 1) {
                a[id++] = x;
                sum += x;
            }
        }
    a[0] = (((1 + m) * m) >> 1) - sum;
    for (int i = 0; i < m; ++i)
        cout << a[i] << (" \n"[i == m - 1]);
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t = 1;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

Problem D. Tung Tung Sahur

题目大意

你面前有两面鼓:一面是左鼓,一面是右鼓。敲击左边的鼓可以记录为 “L”,敲击右边的鼓可以记录为 “R”。

统治这个世界的奇异力量是善变的:有时敲击一次听到了一次,有时敲击一次却听到了两次。因此,敲击左边鼓的声音可能是 “L” 或 “LL”,敲击右边鼓的声音可能是 “R” 或 “RR”。

敲击的顺序记录在字符串 p p p 中,听到的声音记录在字符串 s s s 中。给定 p p p s s s ,判断字符串 s s s 是否可能是字符串 p p p 敲击的结果。

例如,如果 $p = $ “LR”,那么敲击的结果可能是 “LR”、“LRR”、“LLR” 和 “LLRR” 中的任何一个,但字符串 “LLLR” 或 “LRL” 则不可能。

题解思路:双指针

按照题意,对于 p p p 中每个 “L”/“R” , s s s 中必须有至少一个、至多两个同样的字符与之对应。因此我们只需要遍历 p p p ,然后看 s s s 中是否有与之对应的字符满足条件即可。因为 p p p 中 “L” 不可能对应 s s s 中的 “R” ,反之亦然,因此我们每次判断连续相同的字符段即可。时间复杂度为 O ( n + m ) O(n + m) O(n+m)

参考代码(C++)

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1'605;
string p, str;
int n, m;

void solve() {
    cin >> p >> str;
    int n = p.size(), m = str.size();
    int i = 0, j = 0;
    while (i < n) {
        if (j == m || p[i] != str[j]) {
            cout << "NO\n";
            return;
        }
        int k1 = i + 1, k2 = j + 1;
        while (k1 < n && p[k1] == p[i])
            ++k1;
        while (k2 < m && str[k2] == str[j])
            ++k2;
        int ca = k1 - i, cb = k2 - j;
        if (ca > cb || cb > (ca << 1)) {
            cout << "NO\n";
            return;
        }
        i = k1;
        j = k2;
    }
    cout << (j == m ? "YES\n" : "NO\n");
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t = 1;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

Problem E. Boneca Ambalabu

题目大意

博尼卡-安巴拉布给你一个由 n n n 个整数 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,,an 组成的序列。

请输出所有 1 ≤ k ≤ n 1 \leq k \leq n 1kn ( a k ⊕ a 1 ) + ( a k ⊕ a 2 ) + … + ( a k ⊕ a n ) (a_k \oplus a_1) + (a_k \oplus a_2) + \ldots + (a_k \oplus a_n) (aka1)+(aka2)++(akan) 的最大值。注意 ⊕ \oplus 表示 bitwise XOR 运算。

题解思路:枚举+位运算

根据题目给定计算公式以及 ⊕ \oplus 性质,我们只需要知道,在二进制状态下,某一位的结果与其对应的其他数该位情况相关,并且与该位分别有多少个数相关,有多少个数答案就是几倍。因为我们只需要计数每一位对应多少个数,然后枚举序列每个数,计算得到的结果取最大即可。时间复杂度为 O ( n ) O(n) O(n) (实际为 30 n 30n 30n ,忽略常数,但运行时间需要把 30 30 30 考虑在内)。

参考代码(C++)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int maxn = 200'005;
int a[maxn], cnt[30];
int n;

void solve() {
    cin >> n;
    for (int i = 0; i < 30; ++i)
        cnt[i] = 0;
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
        for (int j = 0; j < 30; ++j)
            if ((a[i] >> j) & 1)
                ++cnt[j];
    }
    ll ans = 0;
    for (int i = 0; i < n; ++i) {
        ll res = 0;
        for (int j = 0; j < 30; ++j) {
            int mask = (a[i] >> j) & 1;
            if (mask)
                res += (1LL << j) * (n - cnt[j]);
            else
                res += (1LL << j) * cnt[j];
        }
        ans = max(ans, res);
    }
    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t = 1;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

Problem F. Trulimero Trulicina

题目大意

楚里西纳给出整数 n n n m m m k k k 。可以保证 k ≥ 2 k \geq 2 k2 n ⋅ m ≡ 0 ( m o d k ) n \cdot m \equiv 0 \pmod{k} nm0(modk)

输出一个 n n n 乘以 m m m 的整数网格,使得下列条件均成立:

  • 网格中的每个整数都在 1 1 1 k k k 之间(包括首尾两个整数)。
  • 1 1 1 k k k 的每个整数出现的次数相同。
  • 共享边的两个单元格中没有相同的整数。

可以证明这样的网格总是存在的。如果有多个解,请输出任意一个。

题解思路:构造

我们会想当然地采取循环形式,如 [ 1 , 2 , … , k − 1 , k , 1 , 2 , … , k − 1 , k , 1 , 2 , … , k − 1 , k ] [1, 2, \ldots, k - 1, k, 1, 2, \ldots, k - 1, k, 1, 2, \ldots, k - 1, k] [1,2,,k1,k,1,2,,k1,k,1,2,,k1,k] ,然后从左往右从上往下依次填入网格。这样,网格左右肯定不同,相差1个数。那么上下是否相同呢?假如上面为 x x x ,那么根据上面的填充方式,下面为 y = ( x + m )   m o d   k y = (x + m) \bmod k y=(x+m)modk ,那么只要 m   m o d   k m \bmod k mmodk 不等于零,那么 y y y 肯定在 x x x 右侧,上下也是不同的,符合题意。但如果恰好等于零,怎么办呢?这种情况相当于每行都是序列 [ 1 , 2 , … , k − 1 , k ] [1, 2, \ldots, k - 1, k] [1,2,,k1,k] 重复 m k \frac{m}{k} km 次,即每一行完全一样。既然这样,我把第2行往左边循环移位一个位置,那么不就刚好错开了吗?是的,这样上下也就相差了1个数。以此类推,第468等等偶数行也错开不就满足条件了。

综上所述,问题得解。时间复杂度为 O ( n m ) O(nm) O(nm)

参考代码(C++)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int maxn = 200'005;
vector<int> adj[maxn];
int n, m, k;

void calc(int n, int m, int k) {
    bool f = m % k == 0;
    int cur = 0;
    for (int i = 0; i < n; ++i) {
        adj[i].clear();
        if (f) {
            if (i & 1)
                cur = 1;
            else
                cur = 0;
        }
        for (int j = 0; j < m; ++j) {
            adj[i].push_back(cur + 1);
            cur = (cur + 1) % k;
        }
    }
    for (int i = 0; i < n; ++i)
        for (int j = 0; j < m; ++j)
            cout << adj[i][j] << (" \n"[j == m - 1]);
}

void solve() {
    cin >> n >> m >> k;
    calc(n, m, k);
}

bool check(int n, int m) {
    for (int i = 0; i < n; ++i)
        for (int j = 0; j < m; ++j) {
            if (i && adj[i][j] == adj[i - 1][j])
                return true;
            if (j && adj[i][j] == adj[i][j - 1])
                return true;
        }
    return false;
}

void test() {
    for (int i = 1; i < 30; ++i)
        for (int j = 1; j < 30; ++j)
            for (int k = 2; k * k <= i * j; ++k)
                if (i * j % k == 0) {
                    calc(i, j, k);
                    if (check(i, j))
                        cout << i << ' ' << j << ' ' << k << '\n';
                }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    // test();
    int t = 1;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

Problem G. Chimpanzini Bananini

题目大意

奇潘齐尼-巴纳尼尼站在了一场重大战役的边缘,这场战役注定会带来最终的胜利。

对于长度为 m m m 的任意数组 b b b ,我们把数组的粗糙度记为 ∑ i = 1 m b i ⋅ i = b 1 ⋅ 1 + b 2 ⋅ 2 + b 3 ⋅ 3 + … + b m ⋅ m \sum_{i=1}^m{b_i \cdot i} = b_1 \cdot 1 + b_2 \cdot 2 + b_3 \cdot 3 + \ldots + b_m \cdot m i=1mbii=b11+b22+b33++bmm

Chimpanzini Bananini 送给你一个空数组。你可以对它进行三种操作。

  1. 对数组进行循环移位。也就是说,数组 [ a 1 , a 2 , … , a n ] [a_1, a_2, \ldots, a_n] [a1,a2,,an] 变成 [ a n , a 1 , a 2 , … , a n − 1 ] [a_n, a_1, a_2, \ldots, a_{n-1}] [an,a1,a2,,an1]
  2. 反转整个数组。即数组 [ a 1 , a 2 , … , a n ] [a_1, a_2, \ldots, a_n] [a1,a2,,an] 变成 [ a n , a n − 1 , … , a 1 ] [a_n, a_{n-1}, \ldots, a_1] [an,an1,,a1]
  3. 在数组末尾添加一个元素。在数组的末尾添加 k k k 之后,数组 [ a 1 , a 2 , … , a n ] [a_1, a_2, \ldots, a_n] [a1,a2,,an] 变为 [ a 1 , a 2 , … , a n , k ] [a_1, a_2, \ldots, a_n, k] [a1,a2,,an,k]

每次操作后,您都想计算数组的粗糙度。

请注意,所有操作都是持久的。这意味着每次操作都会修改数组,后续操作应在前一次操作后应用于数组的当前状态。

题解思路:双端队列+数学

我们来一个操作一个操作地分析。我们先假设操作前的粗糙度为 f ( n ) = d f(n) = d f(n)=d ,对于操作一,操作后粗糙度如下。
f ′ ( n ) = a n ⋅ 1 + a 1 ⋅ 2 + a 2 ⋅ 3 + … + a n − 1 ⋅ n = a n ⋅ 1 + a 1 + a 2 + ⋯ + a n − 1 + a 1 ⋅ 1 + a 2 ⋅ 2 + … + a n − 1 ⋅ ( n − 1 ) f'(n) = a_n \cdot 1 + a_1 \cdot 2 + a_2 \cdot 3 + \ldots + a_{n - 1} \cdot n = a_n \cdot 1 + a_1 + a_2 + \cdots + a_{n - 1} + a_1 \cdot 1 + a_2 \cdot 2 + \ldots + a_{n - 1} \cdot (n - 1) f(n)=an1+a12+a23++an1n=an1+a1+a2++an1+a11+a22++an1(n1)
化简后可得。
f ′ ( n ) = ∑ i = 1 n − 1 a i + d − a n ⋅ ( n − 1 ) f'(n) = \sum_{i = 1}^{n - 1}{a_i} + d - a_n \cdot (n - 1) f(n)=i=1n1ai+dan(n1)
看起来我们只需要知道当前数组的和 s u m sum sum 、最后一个元素 a n a_n an 以及粗糙度 d d d 就能快速计算出 f ′ ( n ) = d + s u m − a n ⋅ n f'(n) = d + sum - a_n \cdot n f(n)=d+sumann 。那么支持从尾部删除和头部插入的数据结构用什么呢?显然,双端队列即可。

对于操作二,翻转整个数组每次都要 O ( n ) O(n) O(n) 时间复杂度,显然不可接受。那么怎么办呢?基于操作一的灵感,我们倒着维护一份一模一样的双端队列是不是可以呢?显然是可行的,这样当翻转时,我们切换一下当前双端队列即可,只需要 O ( 1 ) O(1) O(1) 时间复杂度,可以接受。那么对于操作一,此队列(形如 [ a n , a n − 1 , ⋯   , a 2 , a 1 ] [a_n, a_{n - 1}, \cdots, a_2, a_1] [an,an1,,a2,a1] )的粗糙度如何变化呢?我们假设操作前的粗糙度为 g ( n ) = d ′ g(n) = d' g(n)=d 、当前数组的和为 s u m sum sum (和肯定是一样的),那么操作一相当于把这里的 a n a_n an 放到队列的尾部,操作后粗糙度如下。
g ′ ( n ) = a n − 1 ⋅ 1 + ⋯ + a 2 ⋅ ( n − 2 ) + a 1 ⋅ ( n − 1 ) + a n ⋅ n = a n − 1 ⋅ 2 + ⋯ + a 2 ⋅ ( n − 1 ) + a 1 ⋅ n − a n − 1 − ⋯ − a 2 − a 1 + a n ⋅ n g'(n) = a_{n - 1} \cdot 1 + \cdots + a_2 \cdot (n - 2) + a_1 \cdot (n - 1) + a_n \cdot n = a_{n - 1} \cdot 2 + \cdots + a_2 \cdot (n - 1) + a_1 \cdot n - a_{n - 1} - \cdots - a_2 - a_1 + a_n \cdot n g(n)=an11++a2(n2)+a1(n1)+ann=an12++a2(n1)+a1nan1a2a1+ann
化简后可得。
g ′ ( n ) = d ′ − a n ⋅ 1 − ∑ i = 1 n − 1 a i + a n ⋅ n = d ′ + a n ⋅ n − s u m g'(n) = d' - a_n \cdot 1 - \sum_{i = 1}^{n - 1}{a_i} + a_n \cdot n = d' + a_n \cdot n - sum g(n)=dan1i=1n1ai+ann=d+annsum

对于操作三,添加一个元素 x x x ,相当于往正向队列尾部添加一个元素,往逆向队列头部添加一个元素。根据上面分析,正向队列粗糙度为 f ( n ) f(n) f(n) 、逆向队列粗糙度为 g ( n ) g(n) g(n) 以及数组元素和为 s u m sum sum ,那么有如下公式。
f ( n ) = a 1 ⋅ 1 + a 2 ⋅ 2 + ⋯ + a n ⋅ n f(n) = a_1 \cdot 1 + a_2 \cdot 2 + \cdots + a_n \cdot n f(n)=a11+a22++ann
g ( n ) = a n ⋅ 1 + ⋯ + a 2 ⋅ ( n − 1 ) + a 1 ⋅ n g(n) = a_n \cdot 1 + \cdots + a_2 \cdot (n - 1) + a_1 \cdot n g(n)=an1++a2(n1)+a1n
s u m = a 1 + a 2 + ⋯ + a n sum = a_1 + a_2 + \cdots + a_n sum=a1+a2++an
操作后,有如下公式。
f ′ ( n ) = a 1 ⋅ 1 + a 2 ⋅ 2 + ⋯ + a n ⋅ n + x ⋅ ( n + 1 ) = f ( n ) + x ⋅ ( n + 1 ) f'(n) = a_1 \cdot 1 + a_2 \cdot 2 + \cdots + a_n \cdot n + x \cdot (n + 1) = f(n) + x \cdot (n + 1) f(n)=a11+a22++ann+x(n+1)=f(n)+x(n+1)
g ′ ( n ) = x ⋅ 1 + a n ⋅ 2 + ⋯ + a 2 ⋅ n + a 1 ⋅ ( n + 1 ) = g ( n ) + s u m + x g'(n) = x \cdot 1 + a_n \cdot 2 + \cdots + a_2 \cdot n + a_1 \cdot (n + 1) = g(n) + sum + x g(n)=x1+an2++a2n+a1(n+1)=g(n)+sum+x
s u m ′ = s u m + x sum' = sum + x sum=sum+x

综上所述,我们按照分析模拟即可。以上每个操作时间复杂度均为 O ( 1 ) O(1) O(1) ,因此整体时间复杂度为 O ( q ) O(q) O(q)

参考代码(C++)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int maxn = 100'005;
int q;

void solve() {
    cin >> q;
    deque<int> dq[2];
    ll sum = 0, ans[2] = {0};
    int cur = 0, op, x;
    while (q--) {
        cin >> op;
        if (op == 3) {
            cin >> x;
            dq[cur].push_back(x);
            sum += x;
            ans[cur] += 1LL * dq[cur].size() * x;
            dq[cur ^ 1].push_front(x);
            ans[cur ^ 1] += sum;
        } else if (op == 2)
            cur ^= 1;
        else if (!dq[cur].empty()) {
            int siz = dq[cur].size();
            x = dq[cur].back();
            // cout << "siz:" << siz << "; x:" << x << '\n';
            dq[cur].pop_back();
            dq[cur].push_front(x);
            ans[cur] += sum - 1LL * siz * x;
            dq[cur ^ 1].pop_front();
            dq[cur ^ 1].push_back(x);
            ans[cur ^ 1] += 1LL * siz * x - sum;
        }
        cout << ans[cur] << '\n';
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t = 1;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

Problem H. La Vaca Saturno Saturnita

题目大意

Saturnita 的情绪取决于一个长度为 n n n 的数组 a a a 和一个只有他知道如何计算的函数 f ( k , a , l , r ) f(k, a, l, r) f(k,a,l,r) 。下面是他的函数 f ( k , a , l , r ) f(k, a, l, r) f(k,a,l,r) 的伪代码。

int ans = 0;
for (int i = l; i <= r; ++i) {
    while (k % a[i] == 0)
        k /= a[i];
    ans += k;
}
cout << ans << '\n';

给你 q q q 个查询,每个查询都包含整数 k k k l l l r r r 。请为每个查询输出 f ( k , a , l , r ) f(k, a, l, r) f(k,a,l,r)

题解思路:枚举+二分+数论

**注意此题时限是4s。**由于 1 ≤ q ≤ 5 ⋅ 1 0 4 1 \leq q \leq 5 \cdot 10^4 1q5104 1 ≤ k ≤ 1 0 5 1 \leq k \leq 10^5 1k105 ,显然我们依据数论知识可以用时间复杂度为 O ( k ) O(\sqrt{k}) O(k ) 的方法算出 k k k 的所有因子。

观察上述伪代码,对于每个因子,其会一直除 k k k ,直到除不尽。那么对于区间 [ l , r ] [l, r] [l,r] 内处理后的 k k k 是怎么样的呢?它一定是形如 [ k 1 , k 1 , … , k 2 , k 2 , … , k m , k m , … ] [k1, k1, \ldots, k2, k2, \ldots, k_m, k_m, \ldots] [k1,k1,,k2,k2,,km,km,] 这样的。那么什么时候 k k k 才会变化呢?显然遇到一个因子就会变化。因此,我们只需要找到区间 [ l , r ] [l, r] [l,r] 内所有的因子,按位置从小到大枚举出所有的 k i k_i ki 即可,只需要记录上一个位置在哪里即可得到段的长度 l i l_i li ,答案就是 ∑ i = 1 m k i ⋅ l i \sum_{i = 1}^m{k_i \cdot l_i} i=1mkili

根据上面的分析,因子很容易计算,那么怎么找到合适的因子呢?我们只需要把题目给定因子的所有位置按照从小到大顺序存起来,就可以用二分来查找处于区间 [ l , r ] [l, r] [l,r] 的首个位置。

综上所述,问题得解。时间复杂度为 O ( q k l o g n ) O(q \sqrt{k} logn) O(qk logn)

参考代码(C++)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int maxn = 100'005;
int a[maxn];
int n, q;

void solve() {
    cin >> n >> q;
    map<int, vector<int>> adj;
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
        adj[a[i]].push_back(i);
    }
    int k, l, r;
    while (q--) {
        cin >> k >> l >> r;
        --l, --r;
        if (k == 1)
            cout << (r - l + 1) << '\n';
        else {
            vector<int> vi, vp;
            for (int i = 2; i * i <= k; ++i)
                if (k % i == 0) {
                    vi.push_back(i);
                    int j = k / i;
                    if (j != i)
                        vi.push_back(j);
                }
            vi.push_back(k);
            for (int& x : vi)
                if (adj.count(x)) {
                    auto& v = adj[x];
                    auto it = lower_bound(v.begin(), v.end(), l);
                    if (it != v.end() && *it <= r)
                        vp.push_back(*it);
                }
            sort(vp.begin(), vp.end());
            ll ans = 0;
            int last = l;
            for (int& id : vp) {
                int x = k;
                while (x % a[id] == 0)
                    x /= a[id];
                int cnt = id - last;
                ans += 1LL * cnt * k;
                last = id;
                k = x;
            }
            ans += 1LL * k * (r + 1 - last);
            cout << ans << '\n';
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t = 1;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

相关文章:

  • 明远智睿SSD2351核心板助力智能监控系统升级
  • 软件架构设计风格
  • 【C++ 】智能指针:内存管理的 “自动导航仪”
  • 最短路径介绍
  • 【论文解读】MSVM-UNet: Multi-Scale Vision Mamba UNet for Medical Image Segmentation
  • 机器学习项目二:帕金森病检测
  • 【C++教程】使用printf语句实现进制转换
  • 前端面试题(八):简述Vue2的响应式原理
  • mysql自动赋值
  • C语言题目自增在前与在后
  • CentOS服务器能ping通却无法yum install:指定镜像源解决
  • 在思科模拟器show IP route 发现Gateway of last resort is not set没有设置最后的通道
  • One-Hot标签编码方法详解
  • 机器学习概述自用笔记(李宏毅)
  • mysql DQL
  • 「小推桌面官方下载」小推桌面TV版-安卓电视版官方免费下载新版
  • 【KWDB创作者计划】_KWDB部署与使用详细版本
  • 批量将多个文件夹转成压缩包,支持设置压缩密码
  • 【数据库系统概论】第3章 SQL(四)视图(超详细)
  • 5 C 程序全流程解析:编写、预处理、编译、汇编、链接、运行与 GCC 指令详解
  • 俄罗斯延长非法滞留外国人限期离境时间至9月
  • 大理杨徐邱再审上诉案宣判:驳回上诉,维持再审一审判决
  • 野猪穿过江苏电视台楼前广场,被抓捕后送往红山森林动物园
  • 靳燕出任中央戏剧学院党委副书记,原任中戏院长助理
  • 伊朗港口爆炸已造成281人受伤
  • “90后”高层建筑返青春:功能调整的技术路径和运营考验