题目链接
给你一个长度大小为 N 的正整数数组,进行 M 次操作,操作有下列三种。
- 给定区间 [l,r] 对区间中所有数字开根号向下取整,即 ai=⌊√ai⌋(l≤i≤r) 。
- 给定区间 [l,r] ,对区间中每个数字加上一个正整数 x 。
- 查询给定区间 [l,r] 的元素和,即求 ∑ri=lai 。
输入描述:
第一行输入两个正整数 N,M(1≤N,M≤2×105) 。
接下来一行输入 N 个正整数 ai(1≤ai≤109) 。
接下来 M 行, 每行首先输入一个正整数 op,(op∈{1,2}) 。
当 op=1 时, 表示操作一, 然后继续输入两个正整数 l,r(1≤l≤r≤N) 表示对 [l,r] 区间开根号向下取 整。
当 op=2 时, 表示操作二, 然后继续输入三个正整数 l,r,x(1≤l≤r≤N,1≤x≤104) 表示给区间 [l,r] 加上一个正整数 x 。
当 op=3 时, 表示操作二,然后继续输入两个正整数 l,r(1≤l≤r≤N) 表示求区间 [l,r] 的区间和。
输出描述:
对于每一个当 op=3, 输出区间和。
示例1
输入
输出
示例2
输入
输出
解题思路
势能线段树
带修改的区间开方,对每个线段树节点维护最大值 mx 和最小值 mn,当 mx−sqrt(mx)=mn−sqrt(mn) 时说明开方后区间中每个数变化相等,将差值提取出来转化为区间加减问题,否则递归其他节点,复杂度分析类似于吉司机线段树
- 时间复杂度:O(m×log2n)
代码
#include <bits/stdc++.h>
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=2e5+5;
int n,m,a[N];
struct Tr
{
int l,r,add;
LL sum,mx,mn;
}tr[N<<2];
void pushup(int p)
{
tr[p].sum=tr[p<<1].sum+tr[p<<1|1].sum;
tr[p].mx=max(tr[p<<1].mx,tr[p<<1|1].mx);
tr[p].mn=min(tr[p<<1].mn,tr[p<<1|1].mn);
}
void pushdown(int p)
{
if(tr[p].add)
{
tr[p<<1].sum+=1ll*tr[p].add*(tr[p<<1].r-tr[p<<1].l+1);
tr[p<<1|1].sum+=1ll*tr[p].add*(tr[p<<1|1].r-tr[p<<1|1].l+1);
tr[p<<1].add+=tr[p].add,tr[p<<1|1].add+=tr[p].add;
tr[p<<1].mx+=tr[p].add,tr[p<<1|1].mx+=tr[p].add;
tr[p<<1].mn+=tr[p].add,tr[p<<1|1].mn+=tr[p].add;
tr[p].add=0;
}
}
void build(int p,int l,int r)
{
tr[p]={l,r};
if(l==r)
{
tr[p].sum=tr[p].mx=tr[p].mn=a[l];
return ;
}
int mid=l+r>>1;
build(p<<1,l,mid),build(p<<1|1,mid+1,r);
pushup(p);
}
void change(int p,int l,int r)
{
if(l<=tr[p].l&&tr[p].r<=r)
{
int dmx=tr[p].mx-(int)sqrt(tr[p].mx);
int dmn=tr[p].mn-(int)sqrt(tr[p].mn);
if(dmx==dmn)
{
tr[p].add-=dmx;
tr[p].mx-=dmx;
tr[p].mn-=dmn;
tr[p].sum-=1ll*dmx*(tr[p].r-tr[p].l+1);
return ;
}
}
pushdown(p);
int mid=tr[p].l+tr[p].r>>1;
if(l<=mid&&tr[p<<1].mx>1)change(p<<1,l,r);
if(r>mid&&tr[p<<1|1].mx>1)change(p<<1|1,l,r);
pushup(p);
}
void add(int p,int l,int r,int x)
{
if(l<=tr[p].l&&tr[p].r<=r)
{
tr[p].sum+=x*(tr[p].r-tr[p].l+1);
tr[p].mx+=x,tr[p].mn+=x;
tr[p].add+=x;
return ;
}
pushdown(p);
int mid=tr[p].l+tr[p].r>>1;
if(l<=mid)add(p<<1,l,r,x);
if(r>mid)add(p<<1|1,l,r,x);
pushup(p);
}
LL ask(int p,int l,int r)
{
if(l<=tr[p].l&&tr[p].r<=r)return tr[p].sum;
pushdown(p);
int mid=tr[p].l+tr[p].r>>1;
LL res=0;
if(l<=mid)res+=ask(p<<1,l,r);
if(r>mid)res+=ask(p<<1|1,l,r);
return res;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
build(1,1,n);
while(m--)
{
int op,l,r,x;
scanf("%d%d%d",&op,&l,&r);
if(op==1)change(1,l,r);
else if(op==2)
{
scanf("%d",&x);
add(1,l,r,x);
}
else
printf("%lld\n",ask(1,l,r));
}
return 0;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!