吉老师线段树(HDU5306)
给定一个数组a,实现三种操作:
1.将[l,r]区间的数修改为min(a_i,t)
2.求[l,r]区间最大值
3.求[l,r]区间和
之前der了几个细节问题,下次左右子节点一定在函数开头算出lchild和rchild。
解法不难理解,使用线段树维护区间和、区间最大值、区间最大值的个数和区间次大值。当要修改的数(t)大于等于区间最大值的时候,直接返回。大于区间次大值但小于区间最大值,我们可以利用已经维护的区间最大值个数去更新区间和,并下放懒标记。否则继续递归。
可以证明时间复杂度是O(mlog^2 n),证明过程详见吉老师当年在国家集训队的论文。
代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n, m;
ll a[1000005];
struct treenode{
ll cha;
ll sum,max,maxcount,secmax;
#define sum(x) tree[x].sum
#define cha(x) tree[x].cha
#define segmax(x) tree[x].max
#define maxc(x) tree[x].maxcount//统计区间内最大值的个数
#define secm(x) tree[x].secmax//区间次大值
}tree[1000005<<2];
void pushup(ll p){
sum(p)=sum(p<<1)+sum(p<<1|1);
if(segmax(p<<1)==segmax(p<<1|1)){
secm(p)=max(secm(p<<1),secm(p<<1|1));
segmax(p)=segmax(p<<1);
maxc(p)=maxc(p<<1)+maxc(p<<1|1);
}
else if(segmax(p<<1)<segmax(p<<1|1)){
secm(p)=max(segmax(p<<1),secm(p<<1|1));
segmax(p)=segmax(p<<1|1);
maxc(p)=maxc(p<<1|1);
}
else{
secm(p)=max(segmax(p<<1|1),secm(p<<1));
segmax(p)=segmax(p<<1);
maxc(p)=maxc(p<<1);
}
}
void pushdown(ll p){
if(cha(p)!=-1)
{
ll lc=p<<1;
ll rc=p<<1|1;
if(cha(p)<segmax(lc)&&cha(p)>secm(lc)){
sum(lc)-=1ll*(segmax(lc)-cha(p))*maxc(lc);
cha(lc)=cha(p);
segmax(lc)=cha(p);
}
if(cha(p)<segmax(rc)&&cha(p)>secm(rc)){
sum(rc)-=1ll*(segmax(rc)-cha(p))*maxc(rc);
cha(rc)=cha(p);
segmax(rc)=cha(p);
}
cha(p)=-1;
}
}
void build(ll s, ll t, ll p) {
//[s,t]区间建树,节点编号为p
cha(p)=-1;
if (s == t) {
secm(p)=-1;
maxc(p)=1;
sum(p) = a[s];
segmax(p) = a[s];
return;
}
ll mid = s + t >>1;
build(s, mid, p<<1);
build(mid + 1, t, p <<1| 1);
pushup(p);
}
ll getsum(ll l, ll r, ll s, ll t, ll p) {
// [l,r] 为查询区间,[s,t] 为当前节点包含的区间,p 为当前节点的编号
if (l <= s && t <= r)
return sum(p);
ll mid = s + t >>1, sum = 0;
pushdown(p);
if (l <= mid) sum += getsum(l, r, s, mid, p <<1);
if (r > mid) sum += getsum(l, r, mid + 1, t, p <<1|1);
return sum;
}
ll getmax(ll l, ll r, ll s, ll t, ll p) {
// [l,r] 为查询区间,[s,t] 为当前节点包含的区间,p 为当前节点的编号
if (l <= s && t <= r)
return segmax(p);
ll mid = (s + t) >>1, sum = 0;
pushdown(p);
if (l <= mid) sum = max(sum,getmax(l, r, s, mid, p <<1));
if (r > mid) sum = max(sum,getmax(l, r, mid + 1, t, p <<1| 1));
return sum;
}
//updateto为区间修改为某个值
void updateto(ll l, ll r, ll c, ll s, ll t, ll p) {
if(c>=segmax(p))return;
if (l <= s && t <= r&&c>secm(p)) {
sum(p)-=1ll*(segmax(p)-c)*maxc(p);
segmax(p)=c;
cha(p)=c;
return;
}
int mid = (s + t) >> 1;
pushdown(p);
if (l <= mid) updateto(l, r, c, s, mid, p<<1);
if (r > mid) updateto(l, r, c, mid + 1, t, p<<1|1 );
pushup(p);
}
int main() {
int _;
scanf("%d",&_);
while(_--) {
ll aa, l, r,t;
scanf("%lld%lld",&m,&n);
for (int i = 1; i <= m; i++) {
scanf("%lld",&a[i]);
}
build(1, m, 1);
while (n--) {
scanf("%lld%lld%lld",&aa,&l,&r);
if (aa == 0) {
scanf("%lld",&t);
updateto(l, r, t, 1, m, 1);
} else if (aa == 1) {
printf("%lld\n",getmax(l, r, 1, m, 1) );
} else {
printf("%lld\n",getsum(l, r, 1, m, 1) );
}
}
}
}
评论