势能线段树模板题二
题目链接
势能线段树模板题二
给你一个长度大小为 \(N\) 的正整数数组,进行 \(M\) 次操作,操作有下列三种。
- 给定区间 \([l, r]\) 对区间中所有数字开根号向下取整,即 \(a_i=\left\lfloor\sqrt{a_i}\right\rfloor(l \leq i \leq r)\) 。
- 给定区间 \([l, r]\) ,对区间中每个数字加上一个正整数 \(x\) 。
- 查询给定区间 \([l, r]\) 的元素和,即求 \(\sum_{i=l}^r a_i\) 。
输入描述:
第一行输入两个正整数 \(N, M\left(1 \leq N, M \leq 2 \times 10^5\right)\) 。
接下来一行输入 \(N\) 个正整数 \(a_i\left(1 \leq a_i \leq 10^9\right)\) 。
接下来 \(M\) 行, 每行首先输入一个正整数 \(o p,(o p \in\{1,2\})\) 。
当 \(o p=1\) 时, 表示操作一, 然后继续输入两个正整数 \(l, r(1 \leq l \leq r \leq N)\) 表示对 \([l, r]\) 区间开根号向下取 整。
当 \(o p=2\) 时, 表示操作二, 然后继续输入三个正整数 \(l, r, x\left(1 \leq l \leq r \leq N, 1 \leq x \leq 10^4\right)\) 表示给区间 \([l, r]\) 加上一个正整数 \(x\) 。
当 \(o p=3\) 时, 表示操作二,然后继续输入两个正整数 \(l, r(1 \leq l \leq r \leq N)\) 表示求区间 \([l, r]\) 的区间和。
输出描述:
对于每一个当 \(o p=3\), 输出区间和。
示例1
输入
10 3
10 10 10 10 10 10 10 10 10 10
3 1 10
1 1 5
3 1 10
输出
100
65
示例2
输入
10 3
10 10 10 10 10 10 10 10 10 10
1 1 5
2 1 10 5
3 1 10
输出
115
解题思路
势能线段树
带修改的区间开方,对每个线段树节点维护最大值 \(mx\) 和最小值 \(mn\),当 \(mx-sqrt(mx)=mn-sqrt(mn)\) 时说明开方后区间中每个数变化相等,将差值提取出来转化为区间加减问题,否则递归其他节点,复杂度分析类似于吉司机线段树
- 时间复杂度:\(O(m\times log^2n)\)
代码
// Problem: 势能线段树模板题二
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/19917/C
// Memory Limit: 524288 MB
// Time Limit: 4000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
// #define int long long
#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;
}