四月之线段树の从入门到入土

4.2 点击进入新世界
题意:有n个人,在插队。输出最后队伍中,所有人的val 。
solution:
因为是插队,每个后来者居上都会影响前面的赋值,所以可以采用逆序更新;
用 val维护权值,pos维护位置,s维护区间空位;
由于他的位置开始,而我个人习惯从1开始,所以预处理的时候pos记得++。
如果可以放的位置没有超过左边空位数目,那么可以往左子树递归,不然往右子树递归。
重点在于往右子树递归时要减去左区间的空位数。

if(pos<=s[kl]) add(lson,pos,val);
else add(rson,pos-s[kl],val);

详细代码

4.3 点击进入新世界
题意:1表示有连续x人住房,输出这次住房的l坐标,2表示[l,r)的人退房。
solution:
lazy 选三种状态,0表示无人居住,-1表示这个区间不是完整的,1表示已经住满。
常见的区间合并,就要注意lsum和rsum数组的更新,以及msum数组的维护。
msum[k] = max( lsum[kr]+rsum[kl] , max ( msum[kl] , msum[kr] ) )

讲一下自己需要注意的点,就是处理区间合并的问题,总是分不清lr和LR的区别,
所以下次统一用一个结构体来写区间合并的问题。
区间合并其实很套路,但是为什么总是要写这么久呢? 反省一下。

详细代码

4.4 点击进入新世界
题意:有h行,w宽度,n张报纸,每张报纸宽度a[i],求每张报纸能放在第几行。
solution:
每个节点维持左右子区间的最大空位。
重点在于query的时候注意值的up,而不是直接return query。

 ll ans = s[kl]>=x ? query(kl,l,mid,x):query(kr,mid+1,r,x);
 s[k]= max(s[kl] ,s[kr]);
 return ans;

详细代码

4.5 点击进入新世界
题意:区间连续严格递增子段长度。
solution:
裸题,用mid和mid+1来标识,这样的话就可以完成区间的衔接。
注意询问区间时左右儿子和本区间片段比较,例如6,6 就要注意长度不能够超过1。

ll query(ll k,ll l,ll r,ll L,ll R){
    if(l>=L&&r<=R) return s[k];
    ll mid = l+r >>1;
    ll ans=0;
    if(L<=mid) ans=max(ans,query(kl,l,mid,L,R));
    if(R>mid) ans=max(ans,query(kr,mid+1,r,L,R));
    if(a[mid]<a[mid+1]) ans=max(ans,min(mid-L+1,sr[kl])+min(R-mid,sl[kr])); //防止区间贡献重复
    return ans;
}

详细代码


我摊牌了,不玩线段树了,玩字符串了。

你可能感兴趣的:(算法竞赛,个人总结,模板,算法,ACM,线段树)