2021CCPC“第一场”网络赛 GCD on Sequence(思维,线段树)

本文解析了一种解决区间问题的方法,利用GCD(最大公约数)找出特定数值区间内至少有两个倍数的计数策略。通过枚举gcd、维护单调递增的区间最大值,利用线段树高效地计算满足条件的区间数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

GCD on Sequence

原题链接

在这里插入图片描述

我们可以发现,假设区间 [ l , r ] [l,r] [l,r] v ( l , r ) = d v(l,r)=d v(l,r)=d ,那么 [ l , r ] [l,r] [l,r] 内肯定至少有两个 d d d 的倍数。我们可以通过从 n n n 1 1 1 去枚举 g c d gcd gcd ,设其为 d d d ,然后找到所有 d d d 的倍数的下标。我们可以先不考虑其它 g c d gcd gcd 对此次的计算影响,只考虑计算 m a x _ g c d = d max\_gcd=d max_gcd=d 的最终答案。那么假设处理出来了其倍数下标排序后为 [ 1 , 3 , 5 , 10 ] [1,3,5,10] [1,3,5,10] ,那么我们怎么计算有多少个区间满足 v ( l , r ) = d v(l,r)=d v(l,r)=d 呢?我们考虑枚举左端点 l l l l ∈ [ 1 , 1 ] l∈[1,1] l[1,1] r r r 3 3 3 及以后皆可; l ∈ [ 2 , 3 ] l∈[2,3] l[2,3] r r r 5 5 5 及其以后, l ∈ [ 4 , 5 ] l∈[4,5] l[4,5] r r r 5 5 5 到尾。到这里应该找到规律了吧,也就是枚举每个下标 p [ i ] p[i] p[i] ,其合理左端点区间为 [ p [ i − 1 ] + 1 , p [ i ] ] [p[i-1]+1,p[i]] [p[i1]+1,p[i]] ,对应的右端点为 [ p [ i + 1 ] , n ] [p[i+1],n] [p[i+1],n] 。 那么现在考虑其它 g c d gcd gcd 的影响,其实也就是更大的 g c d gcd gcd 对计算的影响。我们就想我们是否可以枚举每个左端点进行计算的时候,是否可以把更大的答案的影响消去。那么其实我们对于每个左端点 l l l 维护尚未确定 v v v 值的最大的右端点 m x [ l ] mx[l] mx[l] ,然后我们可以发现一个性质, m x [ l ] mx[l] mx[l] 是单调递增的,因为假设一个左端点 l 1 l_1 l1 的值为 m x [ l 1 ] mx[l_1] mx[l1] ,假设 l 2 > l 1 l_2>l_1 l2>l1 m x [ l 2 ] < m x [ l 1 ] mx[l_2]<mx[l_1] mx[l2]<mx[l1] ,也就是说 [ l 2 , m x [ l 2 ] + 1 ] [l_2,mx[l_2]+1] [l2,mx[l2]+1] 这个区间是已经确定了 v v v 值的,而这个区间一定是 [ l 1 , m x [ l 1 ] ] [l_1,mx[l_1]] [l1,mx[l1]] 的一个子区间,子区间都确定了 v v v 值,那父区间也必确定了 v v v 值,相互矛盾,这个情况不可能发生。所以我们只要用线段树维护 m x [ i ] mx[i] mx[i] 的区间最大值和区间和,我们在枚举 [ p [ i − 1 ] + 1 , p [ i ] ] [p[i-1]+1,p[i]] [p[i1]+1,p[i]] 的时候,就可以通过在线段树上二分的方法找到一个需要更新的左端点的最小值 L L L ,然后就可以将 [ L , p [ i ] ] [L,p[i]] [L,p[i]] 区间覆盖为 p [ i + 1 ] − 1 p[i+1]-1 p[i+1]1 ,然后不断枚举左端点继续往后更新。 更 新 前 的 根 节 点 区 间 和 更新前的根节点区间和 所 有 更 新 都 完 成 后 的 根 节 点 的 区 间 和 所有更新都完成后的根节点的区间和 的差即是这个点的答案。

#include <bits/stdc++.h>
#define lson rt<<1
#define rson (rt<<1)|1

using namespace std;

typedef long long ll;

const int N = 1e5 + 10;

int n, a[N], rk[N];

ll sum[N << 2];
int lz[N << 2], mx[N << 2];

int pos[N], len;

ll ans[N];

void push_up(int rt) {
    sum[rt] = sum[lson] + sum[rson];
    mx[rt] = max(mx[lson], mx[rson]);
}

void push_down(int rt, int l, int r) {
    if (!lz[rt]) return ;
    int mid = l + r >> 1;
    lz[lson] = lz[rson] = lz[rt];
    mx[lson] = mx[rson] = lz[rt];
    sum[lson] = 1ll * (mid - l + 1) * lz[rt];
    sum[rson] = 1ll * (r - mid) * lz[rt];
    lz[rt] = 0;
}

void build(int rt, int l, int r) {
    lz[rt] = 0;
    if (l == r) {
        sum[rt] = n;
        mx[rt] = n;
        return ;
    }
    int mid = l + r >> 1;
    build(lson, l, mid);
    build(rson, mid + 1, r);
    push_up(rt);
}

void update(int rt, int l, int r, int L, int R, int v) {
    if (L <= l && r <= R) {
        sum[rt] = 1ll * (r - l + 1) * v;
        mx[rt] = v;
        lz[rt] = v;
        return ;
    }
    push_down(rt, l, r);
    int mid = l + r >> 1;
    if (mid >= L) update(lson, l, mid, L, R, v);
    if (mid < R) update(rson, mid + 1, r, L, R, v);
    push_up(rt);
}

int fd(int rt, int l, int r, int L, int R, int v) {
    if (l == r) {
        if (l > R || mx[rt] <= v) return -1;
        else if (l < L) return L;
        else return l;
    }
    push_down(rt, l, r);
    int mid = l + r >> 1;
    if (mx[lson] > v) return fd(lson, l, mid, L, R, v);
    else return fd(rson, mid + 1, r, L, R, v);
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
    int T;
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);
        for (int i = 1; i <= n; ++i) {
            scanf("%d", &a[i]);
            rk[a[i]] = i;
        }
        build(1, 1, n);
        for (int i = n; i >= 1; --i) {
            len = 0;
            for (int j = i; j <= n; j += i) {
                pos[++len] = rk[j];
            }
            sort(pos + 1, pos + 1 + len);
            ans[i] = sum[1];
            for (int j = 1; j + 1 <= len; ++j) {
                int l = fd(1, 1, n, pos[j - 1] + 1, pos[j], pos[j + 1] - 1);
                if (~l) {
                    update(1, 1, n, l, pos[j], pos[j + 1] - 1);
                }
            }
            ans[i] -= sum[1];
        }
        for (int i = 1; i <= n; ++i) {
            printf("%lld\n", ans[i]);
        }
    }
}
### 2021 CCPC 华讯杯 内蒙古大学生程序设计竞题解解析 关于2021CCPC第十六届华讯杯内蒙古大学生程序设计竞的题目解析或解题思路,通常可以通过以下几种方式获取相关信息: #### 官方题解与后总结 许多程序设计竞会在比结束后发布官方题解,这些题解通常包含了每道题目的详细解析和标准算法思路[^1]。对于2021年的CCPC华讯杯事,可以尝试访问CCPC官方网站或者相关的竞论坛寻找官方发布的题解文档。 #### 社区讨论与博客分享 除了官方渠道外,参选手及教练常常会在技术博客、社交媒体或编程社区(如洛谷、牛客网、CSDN等)分享他们的解题经验与思路[^2]。通过搜索关键词“2021 CCPC 华讯杯 题解”,可以找到大量由参者撰写的解析文章。 #### 示例代码与算法实现 以下是基于常见竞题目类型提供的一个通用示例代码框架,用于解决可能涉及的算法问题: ```python # 示例:求解最短路径问题(Dijkstra算法) import heapq def dijkstra(graph, start): n = len(graph) dist = [float('inf')] * n dist[start] = 0 heap = [(0, start)] while heap: d, u = heapq.heappop(heap) if d > dist[u]: continue for v, w in graph[u]: if dist[v] > dist[u] + w: dist[v] = dist[u] + w heapq.heappush(heap, (dist[v], v)) return dist ``` 上述代码展示了如何使用Dijkstra算法计算图中从起点到其他所有节点的最短距离,这在许多竞题目中是常见的考点之一[^3]。 #### 数据结构算法技巧 针对程序设计竞中的高频考点,掌握以下数据结构算法技巧尤为重要: - 动态规划 - 贪心算法 - 图论(如最短路径、最小生成树) - 字符串匹配算法(KMP、Trie树) 这些知识点不仅适用于CCPC,还广泛应用于其他类似的算法中[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值