这道线段树的题目真的是超级酸爽!!!
大概题意是这样的,输入1,x,y代表输出x~y的最大值。2,x,y代表输出x~y这段区间的总和。0,x,y,t代表将x~y这段区间中值更改为min(a[i],t)。难点就在第三种情况。可以看出,数据范围10^6,普通线段树的算法解决,区间更新的算法解决不了,因为有的修改有的不改,但是递归到叶子进行修改肯定超时。这时就诞生出了很麻烦的感觉。于是题解大法由此诞生。感觉这种方法超级炫酷了,至少我没看题解的话就不会想到。
就是这样想。首先是这样的,线段树的特点就是维护某一段的特征,那么这道题的目的是求一段区间的max和sum,在更新过程中max是很容易就可以维护的。在更新的时候,很显然,如果这段区间的最大值max都比t要小,那么这段肯定就不用再被修改了,直接return就好了;另一种情况就是有的大,有的小,那么我们想到,线段树是依次向下分支找寻,能不能简单点,利用前边着一个结论呢。由此我们就想到可以维护一个最大值max和次大值second,当 t 比 max 小,但是比 second 大的时候,此时最大值max就要被替换成 t ,而且无需再往下进行更新了,因为没有必要了,直接return就好了;否则的话就要pushdown,让当前id的左右子树的特征(当前id区间的max【id】,second【id】,sum【id】,以及最大值的个数geshu[i])进行修改。在pushdown的时候,reuturn到了适合的id后,我们再pushup,因为id的特征变化是由下边两个所决定的,所以一定要进行这一步操作。这样事实上该修改的特征值都修改了,也是对lazy的一种用法。然后求最大值的时候一定要pushdown,不明白的话就研究一下lazy标记。然后就是正常的查最大值,和了。
还有就是求和的时候,简单方法就是算出最大值的个数,然后进行计算。具体看代码
ps:这道题中间有种种bug,其中在更新次大值的时候,极其容易将mx【id*2】和mx【id*2+1】进行大小比较,小的那一个作为次大值。但是事实上这样子是错误的,因为很可能mx大的,但是次大值se也比另一个mx要大,所以要都比较一边。
还有一个易错的地方,就是因为给的数据太大了,在计算sun的时候,sum[id]=sum[id]-geshu[id]*(mx[id]-t);将括号拆开写反而会WA ,因为超longlong了,这个地方也算是一个坑吧,我深陷此坑~
代码:
#include
using namespace std;
#define lson id*2,l,(l+r)/2
#define rson id*2+1,(l+r)/2+1,r
const int maxn=1000000+10;
long long se[maxn*4],mx[maxn*4];
long long a[maxn];
long long sum[maxn*4];
int geshu[maxn*4];
void pushup(int id)
{
sum[id]=sum[id*2]+sum[id*2+1];
mx[id]=max(mx[id*2],mx[id*2+1]);
if(mx[id*2]>mx[id*2+1])
{
///se[id]=mx[id*2+1];///这个地方是错的!!!!
se[id]=max(se[id*2],mx[id*2+1]);
geshu[id]=geshu[id*2];
}
else if(mx[id*2]==mx[id*2+1])
{
if(se[id*2]>se[id*2+1])
se[id]=se[id*2];
else if(se[id*2]= mx[id])
return;
sum[id]=sum[id]-geshu[id]*(mx[id]-t);
mx[id]=t;
}
void pushdown(int id)
{
putTag(id*2,mx[id]);
putTag(id*2+1,mx[id]);
}
void change(int L,int R,int id,int l,int r,int t)
{
if(t>mx[id])
return;
if(L<=l &&R>=r && t>se[id])
{
putTag(id,t);
return;
}
pushdown(id);
if(L<=(l+r)/2)
change(L,R,id*2,l,(l+r)/2,t);
if(R>(l+r)/2)
change(L,R,id*2+1,(l+r)/2+1,r,t);
pushup(id);
}
int chaxun_max(int L,int R,int id,int l,int r)
{
if(L<=l &&R>=r )
return mx[id];
pushdown(id);
int ans=0;
if(L<=(l+r)/2)
ans=chaxun_max(L,R,lson);
if(R>(l+r)/2)
ans=max(ans,chaxun_max(L,R,rson));
return ans;
}
long long chaxun_sum(int L,int R,int id,int l,int r)
{
if(L<=l && R>=r)
return sum[id];
pushdown(id);
long long sum=0;
if(L<=(l+r)/2)
sum+=chaxun_sum(L,R,lson);
if(R>(l+r)/2)
sum+=chaxun_sum(L,R,rson);
return sum;
}
int main()
{
int T;
int n,m;
int x,y,c,e;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
build(1,1,n);
for(int i=1;i<=m;i++)
{
scanf("%d",&e);
if(e==1)
{
scanf("%d%d",&x,&y);
cout<
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 4554 Accepted Submission(s): 1232
Problem Description
There is a sequence a of length n. We use ai to denote the i-th element in this sequence. You should do the following three types of operations to this sequence.
0 x y t: For every x≤i≤y, we use min(ai,t) to replace the original ai's value.
1 x y: Print the maximum value of ai that x≤i≤y.
2 x y: Print the sum of ai that x≤i≤y.
Input
The first line of the input is a single integer T, indicating the number of testcases.
The first line contains two integers n and m denoting the length of the sequence and the number of operations.
The second line contains n separated integers a1,…,an (∀1≤i≤n,0≤ai<231).
Each of the following m lines represents one operation (1≤x≤y≤n,0≤t<231).
It is guaranteed that T=100, ∑n≤1000000, ∑m≤1000000.
Output
For every operation of type 1 or 2, print one line containing the answer to the corresponding query.
Sample Input
1 5 5 1 2 3 4 5 1 1 5 2 1 5 0 3 5 3 1 1 5 2 1 5
Sample Output
5 15 3 12
Hint
Please use efficient IO method
Author
XJZX
Source
2015 Multi-University Training Contest 2
Recommend
wange2014 | We have carefully selected several similar problems for you: 6297 6296 6295 6294 6293