线段树
多写点骗点访问量
加法好说,开平方就很气了,因为开平方的信息不能合并。这样就导致常规的线段树,分块之类的维护数列和的方法不能用。我们只能另辟蹊径,找寻性质。
我们发现开平方操作会让数字减少得很快,大概六七次就能把一个数变成1。这给我一种在不特意构造数据的情况下,最终数字很容易变得大部分都一样的感觉。
如果没有加法,那只要判断一下区间里有没有非1的,有就暴力做,这样复杂度没问题。考虑有加法,加法会让数字又变得不一样,仔细观察我们发现对于区间修改[l,r],l和r处相邻数字差变大,也就是只有两个地方发生变化。
那我们用线段树维护序列。如果区间里数字都一样就按常规线段树的来做,否则递归进左右子树。不考虑递归进子树的话,时间复杂度是
O(mlogn)
。考虑递归进去,当且仅当有一次修改的端点碰到了这个节点,最多
m
个修改,其实也就是最多有
有一种神奇的数据:343434,开方完是121212,加2后又是343434,这样的特判一下- -
这种基于均摊时间复杂度的线段树好神奇啊……适用的范围大概是一些操作会改变序列的平衡性,另一些操作会恢复序列的平衡性的题目吧……
#include<cstdio>
#include<cmath>
#include<algorithm>
#define N 100005
using namespace std;
namespace runzhe2000
{
typedef long long ll;
typedef double db;
int n, m, a[N];
struct seg
{
ll sum, mx, mi, tag;
}t[N*5];
void pushup(int x)
{
t[x].sum = t[x<<1].sum + t[x<<1|1].sum;
t[x].mx = max(t[x<<1].mx, t[x<<1|1].mx);
t[x].mi = min(t[x<<1].mi, t[x<<1|1].mi);
}
void pushdown(int x, int l, int r)
{
if(!t[x].tag) return; int mid = (l+r)>>1; ll &tag = t[x].tag;
t[x<<1].tag += tag; t[x<<1|1].tag += tag;
t[x<<1].sum += tag * (mid - l + 1); t[x<<1|1].sum += tag * (r - mid);
t[x<<1].mx += tag; t[x<<1|1].mx += tag;
t[x<<1].mi += tag; t[x<<1|1].mi += tag;
tag = 0;
}
void build(int x, int l, int r)
{
if(l == r){t[x].sum = t[x].mx = t[x].mi = a[l]; t[x].tag = 0; return;}
int mid = (l+r)>>1; build(x<<1,l,mid); build(x<<1|1,mid+1,r); pushup(x);
}
void add(int x, int l, int r, int ql, int qr, int v)
{
if(ql <= l && r <= qr){t[x].tag += v; t[x].sum += (ll)(r-l+1) * v; t[x].mi += v; t[x].mx += v; return;}
pushdown(x,l,r); int mid = (l+r)>>1; if(ql <= mid) add(x<<1,l,mid,ql,qr,v); if(mid < qr ) add(x<<1|1,mid+1,r,ql,qr,v); pushup(x);
}
void modi(int x, int l, int r, int ql, int qr)
{
if(ql <= l && r <= qr && t[x].mx - t[x].mi == (int) sqrt((db)t[x].mx) - (int) sqrt((db)t[x].mi))
{
ll v = (ll)sqrt((double)t[x].mx) - t[x].mx;
t[x].tag += v; t[x].sum += (ll)(r-l+1) * v; t[x].mi += v; t[x].mx += v;
return;
}
pushdown(x,l,r); int mid = (l+r)>>1; if(ql <= mid) modi(x<<1,l,mid,ql,qr); if(mid < qr ) modi(x<<1|1,mid+1,r,ql,qr); pushup(x);
}
ll query(int x, int l, int r, int ql, int qr)
{
if(ql <= l && r <= qr) return t[x].sum; int mid = (l+r)>>1; ll ret = 0;
pushdown(x,l,r);
if(ql <= mid) ret += query(x<<1,l,mid,ql,qr);
if(mid < qr) ret += query(x<<1|1,mid+1,r,ql,qr);
return ret;
}
void main()
{
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++) scanf("%d",&a[i]); build(1,1,n);
for(int opt, l, r, x; m--; )
{
scanf("%d",&opt);
if(opt == 1) // add
{
scanf("%d%d%d",&l,&r,&x);
add(1,1,n,l,r,x);
}
else if(opt == 2) // sqrt
{
scanf("%d%d",&l,&r);
modi(1,1,n,l,r);
}
else
{
scanf("%d%d",&l,&r);
printf("%lld\n",query(1,1,n,l,r));
}
}
}
}
int main()
{
runzhe2000::main();
}