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

【蓝桥杯集训·每日一题】Acwing 3729. 改变数组元素

文章目录

  • 一、题目
    • 1、原题链接
    • 2、题目描述
  • 二、解题报告
    • 1、思路分析
    • 2、时间复杂度
    • 3、代码详解
  • 三、知识风暴
  • 一维差分
  • 区间合并

一、题目

1、原题链接

3729. 改变数组元素

2、题目描述

给定一个空数组 V 和一个整数数组 a1,a2,…,an

现在要对数组 V 进行 n 次操作。

第 i 次操作的具体流程如下:

从数组 V 尾部插入整数 0。 将位于数组 V 末尾的ai个元素都变为 1(已经是 1 的不予理会)。
注意:

  • ai 可能为 0,即不做任何改变。
  • ai 可能大于目前数组 V 所包含的元素个数,此时视为将数组内所有元素变为 1。

请你输出所有操作完成后的数组 V。

输入格式

第一行包含整数 T,表示共有 T 组测试数据。

每组数据第一行包含整数 n。

第二行包含 n 个整数 a1,a2,…,an。

输出格式

每组数据输出一行结果,表示所有操作完成后的数组 V,数组内元素之间用空格隔开。

数据范围

1≤T≤20000,1≤n≤2×105,0≤ai≤n,保证一个测试点内所有 n 的和不超过 2×105

输入样例

3
6
0 3 0 0 1 3
10
0 0 0 1 0 5 0 0 0 2
3
0 0 0

输出样例

1 1 0 1 1 1
0 1 1 1 1 1 0 0 1 1
0 0 0

二、解题报告

1、思路分析

思路1差分+区间合并
(1)将每个需要+1的区间合并,得到若干个不重叠的区间。
(2)对每个区间利用差分将每个区间元素值+1
(3)对差分数组求前缀和得到结果数组,输出即可。
思路2:y总思路

思路来源:y总每日一题b站视频链接
y总yyds

(1)直接对每个区间进行差分将每个区间元素值+1
(2)对差分数组求前缀和得到数组,如果数组元素值>=1说明进行了+1操作,直接输出1;否则说明该值没有+1,直接输出0

2、时间复杂度

思路1时间复杂度O(nlogn)(sort()函数、快排时间复杂度nlogn级别)
思路2时间复杂度O(n)

3、代码详解

思路1代码

#include <iostream>
#include <algorithm>
#include <utility>
#include <vector>
#include <cstring>
using namespace std;
typedef pair<int,int> PII;
vector<PII> v;
const int N=200010;
int pos;
int t,n;
int a[N],b[N];
//区间合并
void merge(vector<PII> &vec){
    vector<PII> ans;
    sort(vec.begin(),vec.end());
    int st=-1,ed=-1;
    for(auto i:vec){
        if(ed<i.first){
            if(st!=-1) ans.push_back({st,ed});
            st=i.first,ed=i.second;
        }
        else ed=max(ed,i.second);    }
    if(st!=-1) ans.push_back({st,ed});
    vec=ans;
}
//差分
void insert(int l,int r,int c){
    b[l]+=c;
    b[r+1]-=c;
}
int main(){
    cin>>t;
    while(t--){
        cin>>n;
        for(int i=1;i<=n;i++){
            pos=i;       //pos记录当前数组中元素的个数
            cin>>a[i];
            if(a[i]==0) continue;    //如果需要将0个元素置为1,跳过下面步骤,执行下一次循环
            if(pos<=a[i]){           //如果需要置为1的元素个数超过数组元素个数
                v.push_back({1,pos});        //需置成1的区间为整个数组

            }
            else{
                v.push_back({pos-a[i]+1,pos});   //否则需置成1的区间为数组后a[i]个元素所在区间
            }
        }
        merge(v);
        for(auto i:v){
            insert(i.first,i.second,1);
        }
        //差分数组求前缀和,得到结果数组
        for(int i=1;i<=pos;i++){
            b[i]+=b[i-1];
            cout<<b[i]<<' ';
        }
        cout<<endl;
        memset(b,0,sizeof b);     //注意不要忘记,下一次循环前需将元素置为0
        pos=0;                    //pos也别忘记
        v.clear();                //区间数组也得清空
    } 
    return 0;
}

思路2代码

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=200010;
int t,n;
int a[N],b[N];
//差分
void insert(int l,int r,int c){
    b[l]+=c;
    b[r+1]-=c;
}
int main(){
    cin>>t;
    int pos=0;
    while(t--){
        cin>>n;
        for(int i=1;i<=n;i++){
            pos=i;                 //pos代表当前元素个数
            cin>>a[i];
            if(a[i]==0) continue;    //如果需要把0个元素置为1,直接跳过下面步骤,执行下一次循环
            if(a[i]>=pos){           //如果需要置的元素个数大于等于区间元素数
                insert(1,pos,1);     //将[1,pos]即整个区间加1
            }
            else{
                insert(pos-a[i]+1,pos,1);  //否则只个数组后a[i]个元素加1
            }
        }
        //差分数组求前缀和,得到每个区间加1后的结果数组
        for(int i=1;i<=pos;i++){
            b[i]+=b[i-1];
            if(b[i]>=1) cout<<1<<' ';     //如果某个位置加了大于等于1次个1,输出1
            else cout<<0<<' ';            //如果没有加过1,输出0
        }
        cout<<endl;
        memset(b,0,sizeof b);    //注意不要忘记,下一次循环前需将元素置为0
        pos=0;                   //pos也别忘记
    }
    return 0;
}

三、知识风暴

一维差分

  • 一维差分可以快速地给指定区间的每个数加任意常数c
  • 差分数组顾名思义就是相邻元素之差组成的数组。
  • 我们如果要对某个区间加减任意常数c可以先求其差分数组,然后对差分数组进行操作。设b数组为差分数组。
    操作:对[l,r]区间每个数+cb[l]+=c,b[r+1]-=c
    最后再对差分数组求前缀和即为处理后的原数组。

区间合并

  • 区间合并就是将某些有重叠(或者说是相交)的区间合并。
  • 基本思路:
    1. 将所有需要合并的区间按左端点排好序。
    2. 以排好序的第一个区间开始,看第二个区间是否与第一个区间有重叠,而且右端点比第一个区间大,如果满足则合并,合并操作就是将第一个区间的右端点更新成第二个区间的右端点;如果第二个区间被第一个区间所完全覆盖,则合并后的区间就是第一个区间,不需要操作;如果第二个区间和第一个区间完全没有交集,说明第二个区间的左端点比第一个区间的右端点大,而我们又是按照区间左端点进行排序的,则下一个区间的左端点也比第一个区间的左端点大,说明当前第一个区间已经和后面所有区间都不可能有交集了,所以第一个区间已经合并完成,我们将其放入结果数组中,下一次将剩下区间里的第一个区间进行如上合并操作,直到完成所有区间合并操作,将最后一个区间也放入结果数组,合并完成。

相关文章:

  • 集成电路相关书籍
  • 【项目】Vue3+TS CMS 基本搭建相关配置
  • KDHX-8700无线高压核相相序表
  • AMD发布23.2.1 新驱动 支持开年新作《魔咒之地》
  • JVM类加载机制
  • ACM第一周---周训---题目合集.
  • Java网络编程之UDP和TCP套接字
  • 最最普通程序员,如何利用工资攒够彩礼,成为人生赢家
  • 从事架构师岗位快2年了,聊一聊我对架构的一些感受和看法
  • Windows11去掉不满足系统要求的提示水印
  • 【算法自由之路】 贪心算法
  • HTTP协议——详细讲解
  • java中volatile与synchronized的区别,volatile为什么不能保证原子性
  • 01背包问题 AcWing(JAVA)
  • 植物大战 List——C++
  • 深入了解以太坊
  • (ckeditor+ckfinder用法)Jquery,js获取ckeditor值
  • 「面试题」如何实现一个圣杯布局?
  • 0x05 Python数据分析,Anaconda八斩刀
  • C++入门教程(10):for 语句
  • Django 博客开发教程 16 - 统计文章阅读量
  • leetcode讲解--894. All Possible Full Binary Trees
  • OpenStack安装流程(juno版)- 添加网络服务(neutron)- controller节点
  • PHP 的 SAPI 是个什么东西
  • React-Native - 收藏集 - 掘金
  • Spring Cloud(3) - 服务治理: Spring Cloud Eureka
  • Synchronized 关键字使用、底层原理、JDK1.6 之后的底层优化以及 和ReenTrantLock 的对比...
  • 创建一个Struts2项目maven 方式
  • 基于Volley网络库实现加载多种网络图片(包括GIF动态图片、圆形图片、普通图片)...
  • 理解IaaS, PaaS, SaaS等云模型 (Cloud Models)
  • 浅谈web中前端模板引擎的使用
  • 使用Swoole加速Laravel(正式环境中)
  • 一起参Ember.js讨论、问答社区。
  • 用 Swift 编写面向协议的视图
  • 【干货分享】dos命令大全
  • Android开发者必备:推荐一款助力开发的开源APP
  • kubernetes资源对象--ingress
  • ​【已解决】npm install​卡主不动的情况
  • #gStore-weekly | gStore最新版本1.0之三角形计数函数的使用
  • (办公)springboot配置aop处理请求.
  • (篇九)MySQL常用内置函数
  • (三)centos7案例实战—vmware虚拟机硬盘挂载与卸载
  • (续)使用Django搭建一个完整的项目(Centos7+Nginx)
  • (已解决)报错:Could not load the Qt platform plugin “xcb“
  • (转)MVC3 类型“System.Web.Mvc.ModelClientValidationRule”同时存在
  • .apk文件,IIS不支持下载解决
  • .gitignore文件---让git自动忽略指定文件
  • .net 调用php,php 调用.net com组件 --
  • .net 无限分类
  • .NET/C# 利用 Walterlv.WeakEvents 高性能地定义和使用弱事件
  • /etc/fstab 只读无法修改的解决办法
  • /etc/sudoers (root权限管理)
  • @Autowired和@Resource的区别
  • @TableId注解详细介绍 mybaits 实体类主键注解
  • @Transient注解