2023NOIP A层联测32 红楼 ~ Eastern Dream

news/2024/12/30 1:44:16/

题目大意

给定一个长度为 n n n的序列 a a a,有 m m m次操作,每次操作有两种类型:

  • 1 x y k,对于所有满足 ( i − 1 ) m o d x ≤ y (i-1)\bmod x\leq y (i1)modxy i i i,将 a i a_i ai的值加上 k k k
  • 2 l r,求 ∑ i = l r a i \sum\limits_{i=l}^ra_i i=lrai

数组下标从 1 1 1开始。

1 ≤ n , m ≤ 2 × 1 0 5 , 1 ≤ x , y ≤ n , 1 ≤ a i , k ≤ 1 0 9 1\leq n,m\leq 2\times 10^5,1\leq x,y\leq n,1\leq a_i,k\leq 10^9 1n,m2×105,1x,yn,1ai,k109

时间限制 800 m s 800ms 800ms,空间限制 32 M B 32MB 32MB


题解

前置知识:根号分治

考虑根号分治,将每次修改和查询分为 x ≤ n x\leq \sqrt n xn x > n x>\sqrt n x>n 两种情况来考虑。

x ≤ n x\leq \sqrt n xn 时,设 v i , j v_{i,j} vi,j表示对于所有 ( t − 1 ) m o d i = j (t-1)\bmod i=j (t1)modi=j t t t a t a_t at要加上的值, v s i , j vs_{i,j} vsi,j v i , j v_{i,j} vi,j的前缀和,也就是对于所有 ( t − 1 ) m o d i ≤ j (t-1)\bmod i\leq j (t1)modij t t t a t a_t at要加上的值。那么每次修改是 O ( n ) O(\sqrt n) O(n )的。对于查询,枚举每个 i i i,求在 i i i为模数时区间 [ l , r ] [l,r] [l,r]对答案的贡献,对每个 i i i都可以 O ( 1 ) O(1) O(1)计算贡献,那么时间复杂度为 O ( n ) O(\sqrt n) O(n )

x > n x>\sqrt n x>n 时,要修改的区间数量不超过 n \sqrt n n 个,那么我们可以用线段树来修改和查询,修改的时间复杂度为 O ( n log ⁡ n ) O(\sqrt n\log n) O(n logn),查询的时间复杂度为 O ( log ⁡ n ) O(\log n) O(logn)

这样的总时间复杂度为 O ( m n log ⁡ n ) O(m\sqrt n\log n) O(mn logn),会 TLE \text{TLE} TLE,下面考虑优化。

x ≤ n x\leq \sqrt n xn 的部分的时间复杂度是可以接受的,我们只需要优化 x > n x>\sqrt n x>n 部分的时间复杂度。

注意到单次修改要修改 O ( n ) O(\sqrt n) O(n )个区间,而单次查询只需要查询 1 1 1个区间,我们考虑差分。

p i p_i pi表示 a i a_i ai经过修改后增加了多少,那么我们可以维护 p p p的差分数组 c c c。对于每次对区间 [ l , r ] [l,r] [l,r]的修改,我们只需要修改 c l c_l cl c r + 1 c_{r+1} cr+1的值,是 O ( 1 ) O(1) O(1)的, O ( n ) O(\sqrt n) O(n )次单点修改的时间复杂度是 O ( n ) O(\sqrt n) O(n )的。

对于每次查询 [ l , r ] [l,r] [l,r],我们需要得到 ∑ i = l r p i = ∑ i = l r ∑ j = 1 i c j \sum\limits_{i=l}^rp_i=\sum\limits_{i=l}^r\sum\limits_{j=1}^ic_j i=lrpi=i=lrj=1icj,下面来推一下这个式子:

∑ i = l r ∑ j = 1 i c j = ∑ i = 1 l − 1 c i × ( r − l + 1 ) + ∑ i = l r c i × ( r − i + 1 ) = ( r − l + 1 ) ∑ i = 1 l − 1 c i + ( r + 1 ) ∑ i = l r c i + ∑ i = l r i × c i \sum\limits_{i=l}^r\sum\limits_{j=1}^ic_j=\sum\limits_{i=1}^{l-1}c_i\times (r-l+1)+\sum\limits_{i=l}^rc_i\times (r-i+1)=(r-l+1)\sum\limits_{i=1}^{l-1}c_i+(r+1)\sum\limits_{i=l}^rc_i+\sum\limits_{i=l}^ri\times c_i i=lrj=1icj=i=1l1ci×(rl+1)+i=lrci×(ri+1)=(rl+1)i=1l1ci+(r+1)i=lrci+i=lri×ci

我们可以用分块来维护 c i c_i ci i × c i i\times c_i i×ci,那么每次查询就相当于分块中的区间查询,一次查询的时间复杂度是 O ( n ) O(\sqrt n) O(n )的。用了分块之后,上面的单点修改操作仍然可以 O ( 1 ) O(1) O(1)解决。

总时间复杂度为 O ( m n ) O(m\sqrt n) O(mn )

虽然时间复杂度理论可行,但时限只有 800 m s 800ms 800ms,而且常数比较大,所以还是要卡一下常。

在下面的代码中,因为 c ≤ n c\leq \sqrt n cn 的部分的常数比 c > n c>\sqrt n c>n 的部分的常数大,所以我们可以将根号分治中分类讨论的决策点调小一点,分为 c ≤ n 2 c\leq \dfrac{\sqrt n}{2} c2n c > n 2 c>\dfrac{\sqrt n}{2} c>2n 来分类讨论,这样能快一些。当然,这个决策点还是要根据你的代码来定。

code

#include<bits/stdc++.h>
#define rg register
using namespace std;
const int N=200000,B=1000;
int n,m,bl,a[N+5];
long long ans=0,s[N+5],v[B+5][B+5],sv[B+5][B+5];
long long p1[N+5],w1[B+5],p2[N+5],w2[B+5];
inline int rd(){int t=0;char ch=getchar();while(ch<'0'||ch>'9') ch=getchar();while(ch>='0'&&ch<='9'){t=t*10+ch-'0';ch=getchar();}return t;
}
inline int pos(int i){return (i-1)/bl+1;
}
inline void pt(int x,int k){if(x>n) return;p1[x]+=k;w1[pos(x)]+=k;p2[x]+=1ll*x*k;w2[pos(x)]+=1ll*x*k;
}
inline long long gt1(int l,int r){long long re=0;int vl=pos(l),vr=pos(r);if(vl==vr){for(rg int i=l;i<=r;i++) re+=p1[i];return re;}for(rg int i=l;i<=vl*bl;i++) re+=p1[i];for(rg int i=vl+1;i<=vr-1;i++) re+=w1[i];for(rg int i=vr*bl-bl+1;i<=r;i++) re+=p1[i];return re;
}
inline long long gt2(int l,int r){long long re=0;int vl=pos(l),vr=pos(r);if(vl==vr){for(rg int i=l;i<=r;i++) re+=p2[i];return re;}for(rg int i=l;i<=vl*bl;i++) re+=p2[i];for(rg int i=vl+1;i<=vr-1;i++) re+=w2[i];for(rg int i=vr*bl-bl+1;i<=r;i++) re+=p2[i];return re;
}
int main()
{
//	freopen("scarlet.in","r",stdin);
//	freopen("scarlet.out","w",stdout);n=rd();m=rd();bl=sqrt(n);if(n>=100) bl/=2;for(rg int i=1;i<=n;i++){a[i]=rd();s[i]=s[i-1]+a[i];}for(rg int o=1,tp,k;o<=m;o++){tp=rd();if(tp==1){int x=rd(),y=rd(),k=rd();y=min(y,x-1);if(x<=bl){for(rg int i=0;i<=y;i++) v[x][i]+=k;sv[x][0]=v[x][0];for(rg int i=1;i<x;i++) sv[x][i]=sv[x][i-1]+v[x][i];}else{for(rg int l=1;l<=n;l+=x){pt(l,k);pt(l+y+1,-k);}}}else{int l=rd(),r=rd();ans=s[r]-s[l-1];for(rg int i=1;i<=bl;i++){int vl=(l-1)/i,vr=(r-1)/i,ml=(l-1)%i,mr=(r-1)%i;ans+=sv[i][mr];if(ml>=1) ans-=sv[i][ml-1];ans+=(vr-vl)*sv[i][i-1];}ans+=(r-l+1)*gt1(1,l-1)+(r+1)*gt1(l,r)-gt2(l,r);printf("%lld\n",ans);}}return 0;
}

http://www.ppmy.cn/news/1219590.html

相关文章

Go语言函数底层实现

基于堆栈模式的程序执行模型决定了函数是语言的一个核心元素。分析Go函数的内部实现&#xff0c;对理解整个程序的执行模型有很大好处。研究底层实现有两个方法一种是看语言编译器源码&#xff0c;分析其对函数的各个特性的处理逻辑&#xff0c;一种是反汇编&#xff0c;将可以…

2.4 Windows驱动开发:内核字符串拷贝与比较

在上一篇文章《内核字符串转换方法》中简单介绍了内核是如何使用字符串以及字符串之间的转换方法&#xff0c;本章将继续探索字符串的拷贝与比较&#xff0c;与应用层不同内核字符串拷贝与比较也需要使用内核专用的API函数&#xff0c;字符串的拷贝往往伴随有内核内存分配&…

交换机基础知识之安全配置

交换机在网络基础设施中扮演着重要角色&#xff0c;它促进了设备之间数据包的流动。正因此&#xff0c;采取适当的安全措施来保护网络免受未经授权的访问和潜在攻击至关重要。本文将全面解读交换机基础安全配置知识&#xff0c;并提供实践方案&#xff0c;以保证安全的网络环境…

MessageFormat:格式化字符串

需求 有一个字符串&#xff1a;"[张三]负责的位置[东厂房]发生[烟雾]报警&#xff0c;请[张三]及时前往处理。"。实际需要对[]内的内容进行替换。如果调用String.replace()&#xff0c;那需要执行多次。而使用MessageFormat.format&#xff0c;则可以替换一次即可获…

了解一下知识付费系统的开发流程和关键技术点

知识付费系统的开发既涉及到前端用户体验&#xff0c;又需要强大的后端支持和复杂的付费逻辑。在这篇文章中&#xff0c;我们将深入探讨知识付费系统的开发流程和关键技术点&#xff0c;并提供一些相关的技术代码示例。 1. 需求分析和规划&#xff1a; 在着手开发知识付费系…

k8s 对外服务之 Ingress( LB + ingress)

Ingress 理论 Ingress 简介 service的作用体现在两个方面&#xff0c;对集群内部&#xff0c;它不断跟踪pod的变化&#xff0c;更新endpoint中对应pod的对象&#xff0c;提供了ip不断变化的pod的服务发现机制&#xff1b;对集群外部&#xff0c;他类似负载均衡器&#xff0c;可…

Netty Review - 核心组件扫盲

文章目录 PreNetty Reactor 的工作架构图CodePOMServerClient Netty 重要组件taskQueue任务队列scheduleTaskQueue延时任务队列Future异步机制Bootstrap与ServerBootStrapgroup()channel()option()与childOption()ChannelPipelinebind()优雅地关闭EventLoopGroupChannleChannel…

ARM64 linux并发与同步之内存屏障

1.2 内存屏障 1.2.1 概念理解 原理部分比较苦涩难懂&#xff0c;我们先不过多详细介绍这部分的由来和经过&#xff0c;接下来着重讲解什么用途和实现&#xff1b; ARM64架构中提供了3条内存屏障指令。 数据存储屏障(Data Memory Barrier, DMB)指令。数据同步屏障(Data Synch…