多比特杯武汉工程大学程序设计竞赛——该死的安全感

数论 + 单调栈

没错吧又是区间问题,而且是一个很恶心的区间问题。赶紧去宰了出题人

  1. 首先需要明白任何一个数字 x x x 都可以写成 x = p 1 a 1 × p 2 a 2 × p 3 a 3 × ⋯ × p m a m x = p_{1}^{a_{1}} \times p_{2}^{a_{2}} \times p_{3}^{a_{3}} \times \dots \times p_{m}^{a_{m}} x=p1a1×p2a2×p3a3××pmam 。其中 p 1 ∼ p m p_{1} \sim p_{m} p1pm 都是质数。 a 1 ∼ a m a_{1} \sim a_{m} a1am 都是对应的质数。

  2. 对于一个拥有 n n n 个数字的数组 x x x ,那么有

  • g c d i = 1 n x i = p 1 m i n i = 1 n a 1 , x i × p 2 m i n i = 1 n a 2 , x i × p 3 m i n i = 1 n a 3 , x i × ⋯ × p m m i n i = 1 n a m , x i gcd_{i = 1}^{n}{x_{i}} = p_{1}^{min_{i = 1}^{n}a_{1, x_{i}}} \times p_{2}^{min_{i = 1}^{n}a_{2, x_{i}}} \times p_{3}^{min_{i = 1}^{n}a_{3, x_{i}}} \times \dots \times p_{m}^{min_{i = 1}^{n}a_{m, x_{i}}} gcdi=1nxi=p1mini=1na1,xi×p2mini=1na2,xi×p3mini=1na3,xi××pmmini=1nam,xi 其中 p 1 ∼ p m p_{1} \sim p_{m} p1pm 表示的是数字 x 1 ∼ x n x_{1} \sim x_{n} x1xn 都有的质数; a i , x j a_{i, x_{j}} ai,xj 表示的是数字 x j x_{j} xj 对应的第 i i i 个质数。 m i n i = 1 n a 1 , x i min_{i = 1}^{n}a_{1, x_{i}} mini=1na1,xi 表示的是对于这 n n n 个数字第一个质数的质数的最小值。

  • l c m i = 1 n x i = p 1 m a x i = 1 n a 1 , x i × p 2 m a x i = 1 n a 2 , x i × p 3 m a x i = 1 n a 3 , x i × ⋯ × p m m a x i = 1 n a m , x i lcm_{i = 1}^{n}{x_{i}} = p_{1}^{max_{i = 1}^{n}a_{1, x_{i}}} \times p_{2}^{max_{i = 1}^{n}a_{2, x_{i}}} \times p_{3}^{max_{i = 1}^{n}a_{3, x_{i}}} \times \dots \times p_{m}^{max_{i = 1}^{n}a_{m, x_{i}}} lcmi=1nxi=p1maxi=1na1,xi×p2maxi=1na2,xi×p3maxi=1na3,xi××pmmaxi=1nam,xi 其中 p 1 ∼ p m p_{1} \sim p_{m} p1pm 表示的是数字 x 1 ∼ x n x_{1} \sim x_{n} x1xn 都有的质数; a i , x j a_{i, x_{j}} ai,xj 表示的是数字 x j x_{j} xj 对应的第 i i i 个质数。 m a x i = 1 n a 1 , x i max_{i = 1}^{n}a_{1, x_{i}} maxi=1na1,xi 表示的是对于这 n n n 个数字第一个质数的质数的最大值。

  1. 知道上述结论了之后,现在需要求解的是指数之和,因此可以枚举不同的质数,针对于每一个质数进行答案的求解。

  2. n u m s [ i ] nums[i] nums[i] 表示对于第 i i i 个数字,质数 p r [ j ] pr[j] pr[j] 出现的次数。因此这里可以直接枚举右端点。以最大公约数为例,针对于新进来的第 i i i 个数字,他在区间中,那么就需要考虑当前质数他的指数对最小值的影响。要计算第 i i i 个数字的影响就需要知道前面的最小值、次小值、次次小值等等。因此这里可以使用单调栈来维护,在求解最大公约数的时候栈内维护的是一个单增的序列,只要 n u m s [ i ] nums[i] nums[i] 比栈顶元素大,那么就出栈栈顶元素。因为在枚举栈内元素的时候计算了他们对答案的贡献,因此这里出栈需要把贡献减去。

  3. 最小公倍数和最大公约数的情况类似。

#include <bits/stdc++.h>

#define endl '\n'

using namespace std;

const int mod = 1e9 + 7;
const int N = 10010, M = 50010;

int n;
int a[N];
int nums[N];
int idx, pr[M];
bool is_de[M];

void init(int n) {
    for (int i = 2; i <= n; i ++ ) {
        if (!is_de[i]) pr[ ++ idx] = i;
        is_de[i] = true;
        for (int j = 1; pr[j] <= n / i; j ++ ) {
            is_de[pr[j] * i] = true;
            if (i % pr[j] == 0) break;
        }
    }
}

long long get() {
    long long res = 0, sum = 0;
    stack<pair<int,  int>> stk;
    for (int i = 1; i <= n; i ++ ) {
        while (!stk.empty() && nums[stk.top().first] <= nums[i]) {
            sum -= stk.top().second;
            stk.pop();
        }
        if (stk.empty()) {
            sum += nums[i] * i;
            stk.emplace(i, nums[i] * i);
        } else {
            sum += nums[i] * (i - stk.top().first);
            stk.emplace(i, nums[i] * (i - stk.top().first));
        }
        res = (res + sum) % mod;
    }
    sum = 0;
    stk = stack<pair<int, int>>();
    for (int i = 1; i <= n; i ++ ) {
        while (!stk.empty() && nums[stk.top().first] >= nums[i]) {
            sum += stk.top().second;
            stk.pop();
        }
        if (stk.empty()) {
            sum -= nums[i] * i;
            stk.emplace(i, nums[i] * i);
        } else {
            sum -= nums[i] * (i - stk.top().first);
            stk.emplace(i, nums[i] * (i - stk.top().first));
        }
        res = (res + sum) % mod;
    }
    return res;
}

void solve() {
    init(M - 1);
    cin >> n;
    for (int i = 1; i <= n; i ++ ) {
        cin >> a[i];
    }
    long long res = 0;
    for (int i = 1; i <= idx; i ++ ) {
        for (int j = 1; j <= n; j ++ ) {
            nums[j] = 0;
            while (a[j] % pr[i] == 0) nums[j] ++ , a[j] /= pr[i];
        }
        res = (res + get()) % mod;
    }
    cout << res << endl;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int T = 1;
//    cin >> T;
    while (T--) {
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值