洛谷U525322 优美区间

优美区间

题目描述

有一个长度为 n n n 的数字序列,序列的第 i i i 个数为 a i a_i ai

定义区间 [ l , r ] [l,r] [l,r] 的优美程度为 gcd ⁡ ( a l , a l + 1 , … , a r ) × ∑ i = l r a i \gcd(a_l,a_{l+1},\dots,a_r)\times\sum\limits_{i=l}^ra_i gcd(al,al+1,,ar)×i=lrai

你需要求出长度至少为 k k k 的区间的优美程度的最大值。

输入格式

第一行两个正整数 n , k n,k n,k

第二行 n n n 个正整数,第 i i i 个正整数为 a i a_i ai

输出格式

一行一个整数,表示答案。

样例 #1

样例输入 #1

7 2
2 1 5 4 4 4 2

样例输出 #1

48

提示

1 ≤ k ≤ n ≤ 1 0 6 1\le k\le n\le10^6 1kn106 1 ≤ a i ≤ 1 0 6 1\le a_i\le10^6 1ai106

注意到:对于一个 r r r,不同的 gcd ⁡ ( a l , a l + 1 , … , a r ) \gcd(a_l,a_{l+1},\dots,a_r) gcd(al,al+1,,ar) 至多只有 ⌊ log ⁡ a r ⌋ + 1 \lfloor\log a_r\rfloor+1 logar+1 种。

当我们对于一个 r = x r=x r=x 求出了所有不同的 gcd ⁡ ( a l , a l + 1 , … , a r ) \gcd(a_l,a_{l+1},\dots,a_r) gcd(al,al+1,,ar) 并找到了与之对应的最小的 l l l 后,要求出 r = x + 1 r=x+1 r=x+1 的情况。只需要在 l l l 的集合中插入 x + 1 x+1 x+1,并将对应的 gcd ⁡ \gcd gcd 相同的 l l l 只保留最小的即可。

于是对于每个 r r r,我们都可以求出所有不同的 gcd ⁡ ( a l , a l + 1 , … , a r ) \gcd(a_l,a_{l+1},\dots,a_r) gcd(al,al+1,,ar) 和与之对应的最小的 l l l,依次对这些区间中长度至少为 k k k 的区间计算优美程度,并取最大值即可。

时间复杂度为 Θ ( n log ⁡ 2 a ) \Theta(n\log^2 a) Θ(nlog2a),常数较小,可以通过本题。精细实现或使用科技可以做到 Θ ( n log ⁡ a ) \Theta(n\log a) Θ(nloga) 的复杂度。

暴力思路

常规暴力思路很容易想,但是拿不到满分。即遍历每一个可能的区间,同时计算gcd。
但是这样时间复杂度是 O ( n 2 log ⁡ a ) O(n^2 \log a) O(n2loga)级的。

#include <bits/stdc++.h>
#define endl '\n'
#define int long long
typedef long long ll;
using namespace std;
int a[1000005],p[1000005];
// int g[1000005];
signed main() {
    cin.tie(0)->ios::sync_with_stdio(0);
    int n,k;
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        p[i]=a[i]+p[i-1];
    }   

    int ans = 0;
    for (int i = 1; i <= n - k + 1; i++) {
        int g = a[i];
        for (int j = i; j <= n; j++) {
            g = __gcd(g, a[j]);
            if (j - i + 1 >= k) {
                int sum = p[j] - p[i - 1];
                int tans = g * sum;
                ans = max(ans, tans);
            }
        }
    }

    cout << ans << endl;
    return 0;
}
AC代码:
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
typedef long long ll;
using namespace std;
int a[1000005],p[1000005];
int g[1000005];  //存储左端点gcd
int pos[1000005]; //存储左端点位置
signed main() {
    cin.tie(0)->ios::sync_with_stdio(0);
    int n,k;
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        cin>>a[i];  
        p[i]=a[i]+p[i-1]; // 前缀和
    }   

    int ans = 0;
    int cnt = 0;
    for (int i = 1; i <= n; i++) { // 枚举右端点i
        pos[++cnt]=i;  // 将右端点本身添加进左端点的集合中
        g[cnt]=a[i];
        int tcnt = cnt;
        cnt = 0;
        for (int j = 1; j <= tcnt; j++) {//枚举左端点j
            g[j]=__gcd(g[j],a[i]); // 每个左端点与a[i]gcd
            if(g[j]!=g[cnt]){  // 重新计数 保存每个gcd最小左端点信息
                pos[++cnt]=pos[j];
                g[cnt]=g[j];
                if ( i - pos[j] + 1 >= k) { // 长度满足条件就更新答案
                    int sum = p[i] - p[pos[j]-1];
                    int tans = g[j] * sum;
                    ans = max(ans, tans);
                }
            }
        }
    }

    cout << ans << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值