势能线段树模板题二

题目链接

势能线段树模板题二

给你一个长度大小为 \(N\) 的正整数数组,进行 \(M\) 次操作,操作有下列三种。

  1. 给定区间 \([l, r]\) 对区间中所有数字开根号向下取整,即 \(a_i=\left\lfloor\sqrt{a_i}\right\rfloor(l \leq i \leq r)\)
  2. 给定区间 \([l, r]\) ,对区间中每个数字加上一个正整数 \(x\)
  3. 查询给定区间 \([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;
}
posted @ 2022-10-19 22:39  zyy2001  阅读(47)  评论(0编辑  收藏  举报