Sums of Sliding Window Maximum_abc407F分析与解答

滑动窗口最大值和问题的分析与解答

倒着考虑,考虑每个a_i对哪些k值做出贡献,对一个a_i,定义L_i和R_i为:

 以上笔误:R_i的定义应该是:连续最多R_i个元素比a_i 

如果得到了 L_i和R_i,我们从k的长度从小到大依次看看,a_i对ans[k](也就是长度为k的滑动窗口的最大值的和)有几次贡献:
用maxx表示max(L_i,R_i)用minx表示min(L_i,R_i)。

发现,当k在区间[1,minx+1],a_i对ans[k]的贡献是 k*a_i,这也是说,当k在[1,minx+1]范围内时,区间长度为多少,a_i就在多少个这么长的区间内出现。

当k在区间[minx+2,maxx+1]时,a_i对ans[k]的贡献是 (minx+1)*a_i,(minx+1)是上一个k的区间中k最大时a_i的出现次数。

当k在区间[maxx+2,minx+maxx+2]时,a_i对ans[k]的贡献是 (minx+maxx+2-k)*a_i,初始k=maxx+2时,贡献为minx*a_i,之后随着k增大贡献值越来越小,最后到0。

在图上画出以上变化过程,发现贡献值的变化率:定义G_i=a_i对ans[i]的贡献值-a_i对ans[i-1]的贡献值,G_i要么是a_i(k在第一区间),要么是0(k在第二区间),要么是-a_i(k在第三区间)。对一个a_i,用差分的方法,可以用常数时间修改这三个区间的变化率,对每个i from 1 to n,时间为O(n),用一次前缀和计算每个点的变化率G_i,再通过变化率计算出每个点的贡献值ans[i],这样可以把复杂度控制在O(n)。

计算L[i]和R[i]:

首先考虑一个特殊情况,如果在一个区间内有多个相同元素并且他们都是这个区间的最大值,比如:
9 1 2 3 2 3 3 9

按照之前L_i和R_i的定义,比如对最左边的那个3,其左右扩到最大,也只是区间:1 2 3 2,但实际上,最左边的3在区间1 2 3 2 3 3中也是最大值,这样会遗漏这个3的贡献。

我们略微调整L_i和R_i的定义,R_i不变,将L_i改成:连续最多L_i个元素小于等于a_i ,现在,如果一个区间中最大值有多次出现,只用最靠右的最大值计算对这个区间的贡献。

如何计算L[i]和R[i]呢?将a_i从小到大排序,如果值相同,按序号排序,也就是按照关键字(a_i,i)排序,倒着遍历排序后的序列,建立一个集合保存之前遍历过的a中元素的下标,若当前遍历到的数在原数组a中的下标是id,用lowerbound在集合中寻找大于id的最小元素和小于id的最大元素,这样找到两个边界,根据(a_i,i)排序的话,找到的边界满足以上L_i和R_i的定义。

#include<bits/stdc++.h>
using namespace std;
using ll=long long ;

const ll maxn=2e5+10;
ll n;
ll a[maxn],L[maxn],R[maxn],slope_d[maxn],ans[maxn],slope[maxn];
struct node {
    ll v,id;
    bool operator < (const node &rhs) const {
        if(v==rhs.v) return id<rhs.id;
        return v<rhs.v;
    }
}b[maxn];

int main()
{
    ios::sync_with_stdio(0);cin.tie(0);

    cin>>n;
    for(ll i=1;i<=n;i++) cin>>a[i];

    //求L[i]和R[i]
    for(ll i=1;i<=n;i++) {
        b[i].v=a[i];b[i].id=i;
    }
    stable_sort(b+1,b+1+n);
    set<ll> s={0,n+1};  //guard
    for(ll i=n;i>=1;i--){
        ll id=b[i].id;
        set<ll>::iterator it=s.lower_bound(id);
        ll rb=*it,lb=*(--it);   //right_boundary
        ll r=rb-id-1,l=id-lb-1;
        R[id]=r;
        L[id]=l;
        s.insert(id);
    }
    /*//输出检查L_i和R_i
    for(ll i=1;i<=n;i++){
        printf("i=%lld\n",i);
        printf("L=%lld R=%lld\n",L[i],R[i]);
    }*/

    //用差分修改区间斜率
    for(ll i=1;i<=n;i++) {
        ll xmin=min(L[i],R[i]),xmax=max(L[i],R[i]);
        slope_d[1]+=a[i];
        slope_d[1+xmin+1]-=a[i];
        slope_d[1+xmax+1]-=a[i];
        slope_d[1+xmax+xmin+2]+=a[i];
    }
    //计算每个点的斜率
    for(ll i=1;i<=n;i++){
        slope[i]=slope[i-1]+slope_d[i];
    }
    //通过斜率计算一次方程的值
    for(ll i=1;i<=n;i++){
        ans[i]=ans[i-1]+slope[i];
    }


    for(ll i=1;i<=n;i++){
        cout<<ans[i]<<"\n";
    }
    return 0;
}

<think>我们有一个三维的numpy数组oxc_conn_array,形状为(m,n,n)代码:row_sums=oxc_conn_array.sum(axis=2)功能解释:这个操作是对三维数组oxc_conn_array沿第2个轴(即axis=2)进行求和。由于数组形状为(m,n,n),我们可以理解为有m个n×n的矩阵(子矩阵)。求和操作沿axis=2(也就是第三个轴,因为numpy的轴索引从0开始,所以axis=0对应第一维,axis=1对应第二维,axis=2对应第三维)。在这个三维数组中:-第一个轴(axis=0)索引子矩阵(m个)-第二个轴(axis=1)索引每个子矩阵的行(n行)-第三个轴(axis=2)索引每个子矩阵的列(n列)因此,沿axis=2求和,就是对每个子矩阵的每一行进行求和(即对每一行的n个元素求和)。求和结果将是一个二维数组,形状为(m,n)。其中:第i个子矩阵的第j行的和存储在结果数组的[i,j]位置。例如:oxc_conn_array[i,j,:]表示第i个子矩阵第j行的所有元素。对第j行求和:row_sums[i,j]=oxc_conn_array[i,j,0]+oxc_conn_array[i,j,1]+...+oxc_conn_array[i,j,n-1]所以,这句代码的功能是:计算每个子矩阵中每一行的和,结果是一个二维数组,形状为(m,n)。举例说明:假设m=2,n=2,数组如下:子矩阵0:[[1,2],[3,4]]子矩阵1:[[5,6],[7,8]]执行row_sums=oxc_conn_array.sum(axis=2)后:子矩阵0第一行求和:1+2=3子矩阵0第二行求和:3+4=7子矩阵1第一行求和:5+6=11子矩阵1第二行求和:7+8=15因此row_sums=[[3,7],[11,15]]这个结果是一个形状为(2,2)的数组。总结:该代码计算了每个子矩阵中每一行的元素之和。</think> ### 代码功能解析 您提供的代码 `row_sums = oxc_conn_array.sum(axis=2)` 在NumPy中的功能如下: ```python row_sums = oxc_conn_array.sum(axis=2) ``` #### 功能解释: 1. **输入结构**: - `oxc_conn_array` 是一个三维NumPy数组,形状为 `(m, n, n)` - 这表示矩阵包含 `m` 个 `n×n` 的子矩阵 2. **求和操作**: - `sum(axis=2)` 沿第2轴(最后一个维度)执行求和 - 计算每个子矩阵中**每行的元素和** - 结果保留了前两个维度(`m` 和 `n`),消减了最后一个维度 3. **输出结构**: - 返回一个二维NumPy数组,形状为 `(m, n)` - 每个元素 `row_sums[i][j]` 表示: - 第 `i` 个子矩阵(0 ≤ i < m) - 第 `j` 行(0 ≤ j < n)的所有元素之和 #### 示例演示: ```python import numpy as np # 创建2个3×3子矩阵的数组 (m=2, n=3) oxc_conn_array = np.array([ [[1, 2, 3], # 子矩阵0第0行 [4, 5, 6], # 子矩阵0第1行 [7, 8, 9]], # 子矩阵0第2行 [[10, 11, 12], # 子矩阵1第0行 [13, 14, 15], # 子矩阵1第1行 [16, 17, 18]] # 子矩阵1第2行 ]) row_sums = oxc_conn_array.sum(axis=2) print(row_sums) ``` **输出结果**: ``` [[ 6 15 24] # 子矩阵0的行和: [1+2+3=6, 4+5+6=15, 7+8+9=24] [33 42 51]] # 子矩阵1的行和: [10+11+12=33, 13+14+15=42, 16+17+18=51] ``` #### 关键说明: 1. **轴(axis)方向**: - `axis=0`:沿子矩阵方向求和 → 形状变为 `(n, n)` - `axis=1`:沿行方向求和 → 形状变为 `(m, n)` - `axis=2`:沿列方向求和 → 形状变为 `(m, n)` 2. **等效数学表达**: ```math \text{row\_sums}[i][j] = \sum_{k=0}^{n-1} \text{oxc\_conn\_array}[i][j][k] ``` 3. **常见应用场景**: - 神经网络中计算特征图的通道和 - 图像处理中计算每行像素值总和 - 物理解算中计算张量沿特定方向的标量场
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值