先需要想到一个结论:
对于线段树上的一块,进行整体开根和加法(加法不影响结果)的次数越多,区间的最大值与最小值的差越小。
利用这个性质,我们维护一棵线段树,线段树上维护区间最大值、最小值、和、元素个数。
对于建树、区间加法(修改)、区间查询,写法就是朴素的线段树
对于区间开根:
1、若区间最大值和最小值的差为0,即整个区间的所有元素相等。此时开根相当于区间覆盖同一个值。
2、若区间最大值和最小值的差为1,那么分为两种情况(可以简单地证明不会存在别的情况)
(1) sqrt( 最大值 ) - sqrt( 最小值 ) = 1 , 此时开根相当于区间减法。
(2) sqrt( 最大值 ) - sqrt ( 最小值) = 0 , 此时开根相当于区间覆盖。
3、若区间最大值和最小值的差大于等于2,那么继续向下递归暴力修改合并。
Tips:
1、3秒时间要跑100组数据,注意线段树的常数。可以考虑读入、输出优化。
2、long long和开根的精度
3、若一个线段树结点的左儿子和右儿子被相同的树区间覆盖,此时需要将标记上传,否则严重影响效率。
#include
#include
#include
#include
//#define mid (l + r) >> 1
#define ls (t << 1)
#define rs ((t<<1) | 1)
#define N 100050
using namespace std;
typedef long long LL;
struct Tree{ LL max,min,sum,siz; }tr[4*N];
LL A[N],tag[4*N],tag2[4*N];
LL n,m;
LL ans = 0LL;
inline void read(LL &x) {
static char c;
while(!isdigit(c = getchar()));
x = c - '0';
while( isdigit(c = getchar()))
x = x * 10 + (c - '0');
}
void out(LL a) {
if(a > 9) out(a / 10);
putchar(a % 10 + '0');
}
inline Tree merge(Tree p1,Tree p2,int t) {
Tree tmp;
if (tag2[ls] == tag2[rs] && tag2[ls] != -1) {
tag2[t] = tag2[ls];
tag[t] = 0;
} else tag2[t] = -1;
tmp.max = max( p1.max , p2.max );
tmp.min = min( p1.min , p2.min );
tmp.sum = p1.sum + p2.sum;
tmp.siz = p1.siz + p2.siz;
return tmp;
}
void build(int l,int r,int t) {
tag[t] = 0;
if (l == r) {
tr[t].max = tr[t].min = A[l];
tr[t].sum = 1LL * A[l];
tr[t].siz = 1;
tag2[t] = A[l];
return ;
}
tag2[t] = -1;
int mid = (l + r) >> 1;
build(l,mid,ls);
build(mid+1,r,rs);
tr[t] = merge(tr[ls],tr[rs],t);
}
inline void color(int t,LL v) {
tr[t].max += v;
tr[t].min += v;
tr[t].sum += 1LL * tr[t].siz * v;
return ;
}
inline void color2(int t,LL v) {
tr[t].max = v;
tr[t].min = v;
tr[t].sum = 1LL * tr[t].siz * v;
return ;
}
inline void push_down(int t) {
if (tag[t] == 0 && tag2[t] == -1) return ;
assert(!(tag[t] != 0 && tag2[t] != -1));
if (tag2[t] != -1) {
tag[ls] = tag[rs] = 0LL;
tag2[ls] = tag2[t];
color2(ls,tag2[t]);
tag2[rs] = tag2[t];
color2(rs,tag2[t]);
} else {
if (tag2[ls] != -1)
tag2[ls] += tag[t];
else
tag[ls] += tag[t];
color(ls,tag[t]);
if (tag2[rs] != -1)
tag2[rs] += tag[t];
else
tag[rs] += tag[t];
color(rs,tag[t]);
}
tag2[t] = -1; tag[t] = 0;
return ;
}
LL ll,rr; LL v;
void update_sqrt(int l,int r,int t) {
//assert(l <= rr && r >= ll);
//if (l > rr || r < ll) return ;
if (l >= ll && r <= rr) {
if (tag2[t] != -1) {
tag2[t] = 1LL * sqrt( tag2[t] );
tr[t].max = tr[t].min = tag2[t];
tr[t].sum = tr[t].siz * tag2[t];
return ;
}
LL p1 = sqrt( tr[t].max );
LL p2 = sqrt( tr[t].min );
if (tr[t].max - tr[t].min == 1) {
if (p1 - p2 == 1) {
LL v = p1 - tr[t].max;
tag[t] += v;
color(t,v);
} else {
LL v = p1;
tag[t] = 0;
tag2[t] = v;
color2(t,v);
}
return ;
}
}
push_down(t);
int mid = (l + r) >> 1;
if (ll <= mid) update_sqrt(l,mid,ls);
if (rr > mid) update_sqrt(mid+1,r,rs);
tr[t] = merge(tr[ls],tr[rs],t);
}
void update_add(int l,int r,int t) {
//assert(l <= rr && r >= ll);
if (l >= ll && r <= rr) {
if (tag2[t] != -1)
tag2[t] += v;
else
tag[t] += v;
color(t,v);
return ;
}
push_down(t);
int mid = (l + r) >> 1;
if (ll <= mid)
update_add(l,mid,ls);
if (rr > mid)
update_add(mid+1,r,rs);
tr[t] = merge(tr[ls],tr[rs],t);
}
void query(int l,int r,int t) {
//assert(l <= rr && r >= ll);
if (l >= ll && r <= rr) { ans += 1LL * tr[t].sum; return; }
push_down(t);
int mid = (l + r) >> 1;
if (ll <= mid) query(l,mid,ls);
if (rr > mid) query(mid+1,r,rs);
tr[t] = merge(tr[ls],tr[rs],t);
}
int main()
{
LL T = 0; read(T);
while (T--) {
//memset(tr,0,sizeof(tr));
//memset(tag,0,sizeof(tag));
//memset(tag2,255,sizeof(tag2));
read(n); read(m);
for (int i=1;i<=n;i++) read(A[i]);
build(1,n,1);
while (m--) {
LL cmd = 0; read(cmd); read(ll); read(rr);
switch(cmd) {
case 1: { read(v); update_add(1,n,1); break; }
case 2: { update_sqrt(1,n,1); break; }
case 3: { ans = 0LL; query(1,n,1); out(ans); printf("\n"); break; }
assert(0);
}
}
}
return 0;
}