P4117 [Ynoi2018] 五彩斑斓的世界

分析第一个操作

朴素的做法,遍历一遍大于x就-=x,极限复杂度O(mn)

分块做法

整块:我们维护一个最大值,从mx到x遍历一遍(减去x)用并查集操作merge(i,i-x),考虑mx=100001,x=1,极限复杂度O(mV)

我们可以分析

1.x>(mx/2),从mx到x遍历一遍,应该是最优的复杂度O(mx-x)

2.x<(mx/2),最优的复杂度应该是O(x),因此思考在这种情况下如何操作使得复杂度为O(x),从x到0遍历一遍呗,我们可以遍历一遍x到0,merge(i,i+x),打上区间减法标记

3.x==(mx/2)都行

通过势能分析法总复杂度应该是O(sqrt(n)V) (我母鸡)(操作让区间最大值和区间最小值的差值变小,,值域缩小)

考虑每次都是x==(mx/2),且mx==V,O(V/2*sqrt(m))?

散块:暴力操作,再重构,无脑真不错,每次最多重构O(2*sqrt(n)),总共O(m*sqrt(n))

分析第二个操作

整块:直接得到并查集的大小

散块:遍历l到r,查询并查集==x,res++;

分析总时间复杂度O(Vsqrt(n)+msqrt(n)),快O(1e9),卡卡常啦

如果这样操作我们应该需要一个O(V*sqrt(n))数组,开不下这么大的数组;

lxl这个trick很妙啊

因为我们块与块之间没有关系,且时间挺多,可以离线下来,答案还有可加性,所以我们可以计算每个块的贡献这样只要开O(V)的数组即可

因此空间复杂度是O(V),1e6随便开

接下来,我们用代码实现一下。。。

先分析一下我们定义哪些数据 

fa[N]并查集,index[V](存储该值的位置),val[N](存储该位置的值),num[V](存储该值的数量),lazy(减法懒标记),mx(最大值),ans[M](询问的答案)

int a[N];
int fa[N],index[V],num[V],val[N];//fa,该值的位置,大小,该位置的值
int L[B],R[B];//记录每个块的范围[l,r]
int ans[M];
int mx=-INF;
int lazy;//减法懒标记

L,R(设为全局变量也行)

并查集2个操作

1.find

inline int find(int x){
    if(fa[x]==x){
        return x;
    }
    return fa[x]=find(fa[x]);
}

2.merge

合并2个值的位置,2个值的数量,以及合并后要对原数组清空

inline void merge(int x,int y){
    if(index[y]){//值存在
        fa[index[x]]=index[y];//合并
    }else{//不存在
        index[y]=index[x];
        val[index[x]]=y;//赋值为y
    }
    num[y]+=num[x];//合并
    index[x]=0;//清空
    num[x]=0;//清空
}

重构

重新维护一遍之前的量

inline void rebuild(int pos){//记录最大值,合并值,处理值的数量 
    mx=-INF;
    lazy=0;
    for(int i=L[pos];i<=R[pos];i++){
        mx=max(mx,a[i]);
        if(!index[a[i]]){
            index[a[i]]=i;
            fa[i]=i;
            val[i]=a[i];
        }else{
            fa[i]=index[a[i]];
        }
        num[a[i]]++;
    }
}

操作一

1.整块

inline void modify(int x){//区间 O(V)
    if((x<<1)<=(mx-lazy)){//x*2<=mx,
        for(int i=lazy;i<=x+lazy;i++){//a[i]<=x 都加x,lazy+x;O(x);
            if(index[i]){
                merge(i,i+x);
            }
        }
        lazy+=x;
    }else{//2*x>mx
        for(int i=mx;i>x+lazy;i--){//a[i]>x 减去x,O(mx-x);
            if(index[i]){
                merge(i,i-x);
            }
        }
        //最大的值可能有x,与原本的mx取小
        mx=min(mx,x+lazy);
    }
}

min解释一下(我一开始不会),现在的情况是2*x>mx,可以转化成x>mx-x

分析2种情况

1.mx>x && x(数组中存在x),x

2.mx<x,mx

因此要取min

2.散块

我们要只需要下传懒标记,操作完重构一下即可

inline void update(int l,int r,int x,int pos){
    //下传lazy
    for(int i=L[pos];i<=R[pos];i++){
        int temp=val[find(i)];
        a[i]=temp-lazy;
        index[temp]=0;
        num[temp]=0;
    }
    for(int i=L[pos];i<=R[pos];i++){
        val[i]=0;
    }
    l=max(l,L[pos]);
    r=min(r,R[pos]);
    //暴力操作
    for(int i=l;i<=r;i++){
        if(a[i]>x){ 
            a[i]-=x;
        }
    }
    rebuild(pos);
}

操作二

1.整块

直接加num[]

ans[j]+=num[que[j].x+lazy];

2.遍历一遍

inline int query(int l,int r,int x,int pos){
    int res=0;
    l=max(l,L[pos]);
    r=min(r,R[pos]);
    for(int i=l;i<=r;i++){
        if(val[find(i)]-lazy==x){
            res++;
        }
    }
    return res;
}

感觉询问比较轻松,主要是操作维护这块是难点

完整代码

#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<vector>
#define INF 2147483647
using namespace std;
typedef long long ll;
const int N=1e6+9;
const int B=1e4+9;
const int V=1e5+9;
const int M=5e5+9;
namespace Lan {
    inline string sread() {
        string s=" ";char e=getchar();
        while(e==' '||e=='\n')e=getchar();
        while(e!=' '&&e!='\n')s+=e,e=getchar();
        return s;
    }
    inline void swrite(string s){
        for(char e:s)putchar(e);
        printf("\n");
    }
    inline ll read() {
        ll x=0,y=1;char c=getchar();
        while(!isdigit(c)){if(c=='-')y=-1;c=getchar();}
        while(isdigit(c)){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
        return x*=y;
    }
    inline void write(ll x) {
        if(x<0){x=-x,putchar('-');}ll sta[35],top=0;
        do sta[top++]=x%10,x/=10;while(x);
        while(top)putchar(sta[--top]+'0');
    }
    inline int max(int a,int b){
        if(a>b){
            return a;
        }
        return b;
    }
    inline int min(int a,int b){
        if(a>b){
            return b;
        }
        return a;
    }
}using namespace Lan;
int a[N];
int fa[N],index[V],num[V],val[N];//fa,该值的位置,大小,该位置的值
int L[B],R[B];//记录每个块的范围[l,r]
int ans[M];
int mx=-INF;
int lazy;//减法懒标记
struct Q{
    int op,l,r,x;
}que[M];
inline int find(int x){
    if(fa[x]==x){
        return x;
    }
    return fa[x]=find(fa[x]);
}
inline void merge(int x,int y){
    if(index[y]){//值存在
        fa[index[x]]=index[y];//合并
    }else{//不存在
        index[y]=index[x];
        val[index[x]]=y;//赋值为y
    }
    num[y]+=num[x];//合并
    index[x]=0;//清空
    num[x]=0;//清空
}
inline void rebuild(int pos){//记录最大值,合并值,处理值的数量 
    mx=-INF;
    lazy=0;
    for(int i=L[pos];i<=R[pos];i++){
        mx=max(mx,a[i]);
        if(!index[a[i]]){
            index[a[i]]=i;
            fa[i]=i;
            val[i]=a[i];
        }else{
            fa[i]=index[a[i]];
        }
        num[a[i]]++;
    }
}
inline void modify(int x){//区间 O(V)
    if((x<<1)<=(mx-lazy)){//x*2<=mx,
        for(int i=lazy;i<=x+lazy;i++){//a[i]<=x 都加x,lazy+x;O(x);
            if(index[i]){
                merge(i,i+x);
            }
        }
        lazy+=x;
    }else{//2*x>mx
        for(int i=mx;i>x+lazy;i--){//a[i]>x 减去x,O(mx-x);
            if(index[i]){
                merge(i,i-x);
            }
        }
        //最大的值可能有x,与原本的mx取小
        mx=min(mx,x+lazy);
    }
}
inline int query(int l,int r,int x,int pos){
    int res=0;
    l=max(l,L[pos]);
    r=min(r,R[pos]);
    for(int i=l;i<=r;i++){
        if(val[find(i)]-lazy==x){
            res++;
        }
    }
    return res;
}
inline void update(int l,int r,int x,int pos){
    //下传lazy
    for(int i=L[pos];i<=R[pos];i++){
        int temp=val[find(i)];
        a[i]=temp-lazy;
        index[temp]=0;
        num[temp]=0;
    }
    for(int i=L[pos];i<=R[pos];i++){
        val[i]=0;
    }
    l=max(l,L[pos]);
    r=min(r,R[pos]);
    //暴力操作
    for(int i=l;i<=r;i++){
        if(a[i]>x){ 
            a[i]-=x;
        }
    }
    rebuild(pos);
}
inline void init(){
    mx=-INF;
    lazy=0;
    memset(num,0,sizeof(num));
    memset(index,0,sizeof(index));
}
inline bool inrange(int L,int R,int l,int r){//[l[L,R]r]
    return L>=l && R<=r;
}
inline bool outofrange(int L,int R,int l,int r){
    return L>r || l>R;
}
int main(){
    // ios::sync_with_stdio(false);
    // cin.tie(0),cout.tie(0);
    int n,m;
    n=read();
    m=read();
    for(int i=1;i<=n;i++){
        a[i]=read();
    }
    for(int i=1;i<=m;i++){
        que[i].op=read();
        que[i].l=read();
        que[i].r=read();
        que[i].x=read();
    }
    int blo=sqrt(n);
    int t=ceil(n*1.0/blo);
    for(int i=1;i<=t;i++){
        L[i]=(i-1)*t+1;
        R[i]=i*t;
    }
    //逐块处理
    for(int i=1;i<=t;i++){
        init();
        rebuild(i);
        for(int j=1;j<=m;j++){
            if(que[j].l>que[j].r){
                continue;
            }
            if(outofrange(L[i],R[i],que[j].l,que[j].r)){//不在这个区间
                continue;
            }
            if(que[j].op==1){
                if(inrange(L[i],R[i],que[j].l,que[j].r)){//询问区间包含这个块
                    modify(que[j].x);//整块处理
                }else{
                    update(que[j].l,que[j].r,que[j].x,i);//这一块处理完重构
                }
            }else{
                if(que[j].x+lazy>1e5+1){//没有贡献 
                    continue;
                }
                if(inrange(L[i],R[i],que[j].l,que[j].r)){//询问区间包含这一块
                    ans[j]+=num[que[j].x+lazy];
                }else{
                    ans[j]+=query(que[j].l,que[j].r,que[j].x,i);//只能处理这一块的
                }
            }
        }
    }
    for(int i=1;i<=m;i++){
        if(que[i].op==2){
            write(ans[i]);
            putchar('\n');
        }
    }
    return 0;
}

不愧是大分块,lxl orz,还是刷刷简单点的分块题再来挑战ynoi吧

无期稿件P4119 [Ynoi2018] 未来日记

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/527015.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LwIP TCP/IP

LWIP 架构 LwIP 符合 TCP/IP 模型架构&#xff0c;规定了数据的格式、传输、路由和接收&#xff0c;以实现端到端的通信。 此模型包括四个抽象层&#xff0c;用于根据涉及的网络范围&#xff0c;对所有相关协议排序&#xff08;参见图 2&#xff09;。这几层从低到高依次为&am…

【grpc】二、grpc入门,基本使用方法

上篇介绍了使用protobuf生成grpc相关代码&#xff0c;并实现了服务端方法&#xff0c;本篇介绍下具体的使用。 一、服务端 // server.gopackage mainimport ("google.golang.org/grpc""grpcDemo/calc""net" )func startServer(addr string) {//…

近距离共享数字化实战经验,深挖数据价值赋能千行百业

近期&#xff0c;思迈特软件积极投身于金融、制造、零售、医疗等多个行业的线下活动中&#xff0c;深度解析行业趋势&#xff0c;分享BI数字化创新解决方案&#xff0c;并与客户及合作伙伴进行深入交流。通过这些活动&#xff0c;不仅展示了思迈特软件在各领域的先进技术和成果…

【SpringCloud】Nacos 配置管理

目 录 一.统一配置管理1. 在 nacos 中添加配置文件2. 从微服务拉取配置 二.配置热更新1. 方式一2. 方式二 三.配置共享1. 添加一个环境共享配置2. 在 user-service 中读取共享配置3. 运行两个 UserApplication&#xff0c;使用不同的 profile4. 配置共享的优先级5. 多服务共享配…

【C语言】标准库ctype.h(判断和映射字符)

【C语言】标准库ctype.h&#xff1a;判断和映射字符。 #include <ctype.h> 标准库ctype.h&#xff1a; 函数中的参数&#xff1a;都是无符号字符转换为int值进行传递。 判断函数中的返回&#xff1a;若满足判断条件&#xff0c;返回非零值。若不满足判断条件&#xff0c…

计算机研究生规划

一、计算机研究生技术栈 两条腿走路: 左侧工程实践能力&#xff1a;要掌握python编程语言&#xff0c;它和机器学习、神经网络&#xff08;这两门几乎是必须掌握的技能&#xff09;的学习有很大关系 右侧学术创新能力 二、编程语言能力提升 左边基础&#xff0c;右边教你写…

区块链的网络架构有哪些?

区块链技术的兴起正在深刻地改变着互联网的格局。它不仅提供了去中心化、数据透明、难以篡改等优势&#xff0c;还为各种应用场景提供了新的可能性。为了更好地理解区块链&#xff0c;我们需要深入探讨其网络架构。 区块链网络架构主要由以下几个部分组成&#xff1a; 1. 区块…

数字信息化手术麻醉信息系统源码,自动生成各种医疗文书、集成HIS、EMR、LIS、PACS系统

手术麻醉信息系统可以实现手术室监护仪、麻醉机、呼吸机、输液泵等设备输出数据的自动采集&#xff0c;采集的数据能据如实准确地反映患者生命体征参数的变化&#xff0c;并实现信息高度共享&#xff0c;根据采集结果&#xff0c;综合其他患者数据&#xff0c;自动生成手术麻醉…

Vite+Vue3.0项目使用ant-design-vue <a-calendar>日期组件汉化

antd的弹框、日期等默认为英文&#xff0c;要把英文转为中文请看下文&#xff1a; 1.首先我们要在main.js中引入ant-design组件库并全局挂载&#xff1a; import App from ./App import Antd from ant-design-vue; import ant-design-vue/dist/antd.css;const app createApp(…

数据结构与算法:哈希表

目录 1.哈希表和哈希 1.1.知识引入 1.2.为什么需要哈希表呢&#xff1f; 2.简易的哈希表 2.1.哈希表的基础结构 2.2.如何实现基础的哈希表 2.2.1.增 2.2.2.删 2.2.3.查 2.3.泛型编程下的哈希表 3.简易的哈希桶 1.哈希表和哈希 1.1.知识引入 哈希表&#xff08;Hash …

算法打卡day36|动态规划篇04| 01背包理论基础、416. 分割等和子集

目录 01背包理论基础 01背包问题描述 01背包解法 二维数组 一维数组 算法题 Leetcode 416. 分割等和子集 个人思路 解法 动态规划 01背包理论基础 不同的背包种类&#xff0c;虽然有那么多中南背包&#xff0c;但其中01背包和完全背包是重中之重&#xff1b; 01背包问…

练习题(2024/4/8)

1 括号生成 数字 n 代表生成括号的对数&#xff0c;请你设计一个函数&#xff0c;用于能够生成所有可能的并且 有效的 括号组合。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;["((()))","(()())","(())()","()(())",…

GEE必须会教程—一文教会你GEE下载影像数据的方法

一、基本流程 A.平台进入&#xff1a;网站搜索&#xff1a;https://developers.google.com/earth-engine&#xff0c;进入Google Earth Engine 官网平台&#xff08;以下简称GEE平台&#xff09;&#xff0c;正常登录该平台需要利用邮箱进行申请&#xff0c;申请通过后可以正常…

图书馆自助借书机怎么借书

图书馆自助借书机借书流程如下&#xff1a; 1. 找到图书馆自助借书机&#xff0c;在机器上选择借书功能。 2. 输入自己的借书卡号或者身份证号码&#xff0c;如果是第一次借书&#xff0c;可能需要进行注册。 3. 输入图书的条形码号码&#xff0c;可以通过扫描条形码或者手动输…

LangChain-11 Code Writing FunctionCalling 大模型通过编写代码完成需求 大模型计算加法

背景简介 我们知道GPT模型对于内容的输出&#xff0c;是对下一个字符的预测&#xff0c;通过概率选出下一个文本。 而且我们也知道&#xff0c;训练样本是非常庞大的&#xff0c;对于GPT来说&#xff0c;也是有可能学习过1 1 2的。 当我们向GPT询问11 时&#xff0c;完全可以…

Verilog语法——按位取反“~“和位宽扩展的优先级

前言 先说结论&#xff0c;如下图所示&#xff0c;在Verilog中“~ ”按位取反的优先级是最高的&#xff0c;但是在等式计算时&#xff0c;有时候会遇到位宽扩展&#xff0c;此时需要注意的是位宽扩展的优先级高于“~”。 验证 仿真代码&#xff0c;下面代码验证的是“~”按位取…

腾讯云视频点播配置说明 | Modstart

开通云点播 开通云点播 云点播VOD_音视频点播_直播回看_音视频上传、存储转码AI处理方案-腾讯云 获取腾讯云 SecretId 和 SecretSecret 注册并且登录 腾讯云

【深度学习基础】

打基础日常记录 CNN基础知识1. 感知机2. DNN 深度神经网络&#xff08;全连接神经网络&#xff09;DNN 与感知机的区别DNN特点&#xff0c;全连接神经网络DNN前向传播和反向传播 3. CNN结构【提取特征分类】4. CNN应用于文本 RNN基础1. RNN的本质 词向量模型word2Vec1. 自然语言…

LC低通滤波

LC滤波器&#xff0c;是指将电感L与电容器 C进行组合设计构成的滤波电路&#xff0c;可去除或通过特定频率的无源器件。电容器具有隔直流通交流&#xff0c;且交流频率越高越容易通过的特性。而电感则具有隔交流通直流&#xff0c;且交流频率越高越不易通过的特性。因此&#x…

广州等级保护测评公司一览表2024

广州等级保护测评公司一览表2024 序号&#xff1a;1 名称&#xff1a;南方电网科学研究院有限责任公司 地址&#xff1a;广东省广州市萝岗区科学城科翔路11号J1栋3、4、5楼及J3栋3楼 序号&#xff1a;2 名称&#xff1a;广州竞远安全技术股份有限公司 地址&#xff1a;广…