题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5828
题意就是维护一个序列,总共有三种操作:1,在l到r这个区间上加一个数字、2,把l到r这个区间的每个数字都开根号(向下取整)、3,查询l到r这个区间的和。
由于有开根号这个操作,很难对一段连续的数字进行操作。由于开根号开了几次之后很容易把整段数字变得很小,之后加上数字 后或者再继续开根号时,很有可能一整段都是相等的。那么可以想到一个剪枝就是,维护这个区间的最大值最小值,假如最大值和最小值开根号得到的数字相同,那么可以整段进行操作。但是还有一种情况,就是3,4,3,4,3……的时候,如果对这个区间不停地加2,开根号,加2,开根号,那么最后还是会出现o(n²) 的情况。然后我们再想到一个剪枝,就是当最小值最大值相差1的时候,如果开根号的结果也相差1,那么我们可以视为给这段区间同时减去一个数字。这个线段树我们维护一下,区间最值,区间和,以及lazy,cover。
这也算是我写的第一道比较复杂的线段树吧。
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<time.h>
#include<cstdio>
#include<vector>
#include<stack>
#include<queue>
#include<iostream>
using namespace std;
#define LONG long long
const int INF=0x3f3f3f3f;
const LONG MOD=1e9+61;
const double PI=acos(-1.0);
#define clrI(x) memset(x,-1,sizeof(x))
#define clr0(x) memset(x,0,sizeof x)
#define clr1(x) memset(x,INF,sizeof x)
#define clr2(x) memset(x,-INF,sizeof x)
#define EPS 1e-10
#define lson l , mid , rt<< 1
#define rson mid + 1 ,r , (rt<<1)+1
#define root 1, n , 1
const int MAXN = 1e5 + 10 ;
struct Tree{
LONG Sum , Max , Min;
}tree[MAXN<<2];
LONG lazy[MAXN<<2] ;
LONG cov[MAXN<<2] ;
void Push_up(int rt)
{
tree[rt].Sum = tree[rt<<1].Sum + tree[rt<<1|1].Sum;
tree[rt].Max = max(tree[rt<<1].Max , tree[rt<<1|1].Max) ;
tree[rt].Min = min(tree[rt<<1].Min , tree[rt<<1|1].Min ) ;
}
void build_tree( int l ,int r , int rt)
{
lazy[rt] = 0 ;
cov[rt] = 0 ;
if(l == r)
{
scanf("%lld", &tree[rt].Sum) ;
tree[rt].Max = tree[rt].Sum ;
tree[rt].Min = tree[rt].Sum ;
lazy[rt] = tree[rt].Sum ;
cov[rt] = 0;
return ;
}
int mid = (l +r) /2 ;
build_tree(lson) ;
build_tree(rson) ;
Push_up(rt) ;
}
void Push_down(int l , int r , int rt)
{
if(cov[rt] > 0 )
{
int mid = ( l +r )/2 ;
tree[rt<<1].Sum = (LONG )(mid - l + 1)* cov[rt] ;
tree[rt<<1|1].Sum = (LONG )( r - mid) * cov[rt] ;
tree[rt<<1|1].Max = tree[rt<<1].Max = tree[rt<<1|1].Min = tree[rt<<1].Min = cov[rt<<1] = cov[ rt<<1|1] = cov[rt] ;
cov[rt] = lazy[rt<<1|1] = lazy[rt<<1] = 0 ;
//这里不能有return
}
if(lazy[rt] != 0) // rt的lazy本质上是给lazy的儿子用的
{
int mid = (l + r) /2 ;
lazy[rt<<1] += lazy[rt] ;
lazy[rt<<1|1] += lazy[rt] ;
tree[rt<<1].Sum += (LONG )(mid - l + 1) * lazy[rt] ;
tree[rt<<1|1].Sum += (LONG )(r - mid) * lazy[rt] ;
tree[rt<<1].Max += lazy[rt] ;
tree[rt<<1|1].Max += lazy[rt] ;
tree[rt<<1].Min += lazy[rt] ;
tree[rt<<1 | 1].Min += lazy[rt] ;
lazy[rt] = 0 ;
}
}
void Add(int L ,int R , int l , int r , int rt , LONG val )
{
if(L <= l && r <= R)
{
tree[rt].Sum += (LONG ) (r - l + 1)* val ;
tree[rt].Max += val ;
tree[rt].Min += val ;
lazy [rt] += val ;
return ;
}
int mid = (l + r) / 2;
Push_down(l , r , rt) ;
if(L <= mid)
Add( L , R , lson ,val) ;
if(R > mid )
Add(L , R , rson , val ) ;
Push_up( rt) ;
}
void Update( int L , int R , int l , int r , int rt )
{
if(L <= l&& r <= R)
{
if(tree[rt].Max == 1 ) return ;
if( r == l)
{
LONG tmp = (LONG )floor((sqrt( tree[rt].Sum ))) ;
tree[rt].Sum =
tree[rt].Max =
tree[rt].Min = tmp ;
return ;
}
if( tree[rt].Max == tree[rt].Min)
{
LONG temp = tree[rt].Min ;
tree[rt].Max = tree[rt].Min = (LONG) floor(sqrt(temp)) ;
tree[rt].Sum = (LONG ) tree[rt].Max * (r- l + 1) ;
lazy [rt] += tree[rt].Min -temp ;
return ;
}
if(tree[rt].Max - tree[rt].Min == 1)
{
LONG tmp1 =(LONG )floor( sqrt(tree[rt].Min ) ) ;
LONG tmp2 = (LONG) floor(sqrt(tree[rt].Max) );
if(tmp2 - tmp1 == 1)
{
lazy[rt] += tmp1 - tree[rt].Min ;
tree[rt].Sum += (LONG) (r - l + 1)*(tmp1 - tree[rt].Min ) ;
tree[rt].Min = tmp1 ;
tree[rt].Max = tmp2 ;
return ;
}
else
{
cov[rt] = tmp1 ;
tree[rt].Sum = (LONG )( r - l + 1) * tmp1 ;
tree[rt].Min = tmp1 ;
tree[rt].Max = tmp1 ;
lazy [rt] = 0;
return ;
}
}
LONG tmp1 = (LONG )floor(sqrt( tree[rt].Max ) ) ;
LONG tmp2 = (LONG )floor(sqrt( tree[rt].Min ) ) ;
if(tmp1 == tmp2)
{
cov[rt] = tmp1 ;
tree[rt].Sum = (LONG )(r -l + 1) * tmp1 ;
tree[rt].Min = tmp1 ;
tree[rt].Max = tmp2 ;
lazy[rt] = 0 ;
return ;
}
int mid = (r + l ) / 2 ;
Push_down(l , r , rt ) ;
if(L <= mid)
Update( L , R , lson ) ;
if(R > mid )
Update( L , R , rson ) ;
Push_up(rt) ;
return ;
}
int mid = (l +r) / 2;
Push_down(l , r, rt) ;
if( L <= mid)
Update(L , R ,lson );
if( R > mid)
Update( L , R , rson );
Push_up(rt) ;
}
LONG Query( int L , int R , int l , int r , int rt)
{
if( L <= l && r <= R)
{
return tree[rt].Sum ;
}
int mid = (l +r) /2 ;
Push_down(l , r ,rt ) ;
LONG res = 0 ;
if(L <= mid)
res += Query( L , R , lson );
if( R > mid)
res += Query( L , R , rson ) ;
return res ;
}
int main()
{
// freopen("C:\\Users\\ZhangYuyang\\Desktop\\in.txt","r",stdin);
// freopen("C:\\Users\\ZhangYuyang\\Desktop\\out2.txt","w",stdout);
int T;
cin>>T;
while(T--)
{
int n , m;
cin>>n>>m;
build_tree(root) ;
int op ;
int l , r , x;
for(int i = 1; i<= m ;++i )
{
scanf("%d",&op) ;
if(op == 1)
{
scanf("%d%d%d",&l,&r,&x) ;
Add(l , r , root ,(LONG) x) ;
}
else if(op == 2)
{
scanf("%d%d",&l,&r);
Update( l ,r , root ) ;
}
else
{
scanf("%d%d" ,&l,&r ) ;
printf("%lld\n", Query( l, r , root ) ) ;
}
}
}
}