(为啥没有第五周?去问国庆。)
周任务:
算法任务:
朴素线段树
具体任务:
线段树就是一种比较超乎萌新(我)想象的数据结构吧,初识就觉得“哇,好难,好厉害的亚子”,用多了就觉得还好。它的关键在于一种区间的思想,lazy标记的使用的那种延迟下传的思想,能活用这种思想才是学习线段树的作用吧。
好用的板子千篇一律,有趣的bug万行难求。为了不影响各位的“找bug”或者“找我的板子到底和别人的板子有啥不同”的体验,我就不给模板了其实是自己没写。
废话也不多说了——上代码。
hdu1166——敌兵布阵(单点修改,区间查询,树状数组同样适用)
代码:
#include<iostream>
#include<cstdio>
#include<string>
using namespace std;
const int maxn = 1e5+5;
int Tree[maxn<<2],T,N,ans;
string Str;
void BuildTree(int L,int R,int index){
if(L==R){
scanf("%d",&Tree[index]);
return ;
}
int Mid = (L+R)>>1;
BuildTree(L,Mid,index<<1);
BuildTree(Mid+1,R,index<<1|1);
Tree[index] = Tree[index<<1] + Tree[index<<1|1];
}
void pAdd(int p,int v,int index,int L,int R){
if(L==R){
Tree[index]+=v;
return;
}
int Mid = (L+R)>>1;
if(p<=Mid)pAdd(p,v,index<<1,L,Mid);
else pAdd(p,v,index<<1|1,Mid+1,R);
Tree[index] = Tree[index<<1]+Tree[index<<1|1];
}
void dQuery(int l,int r,int index,int L,int R){
if(l<=L&&R<=r){
ans+=Tree[index];
return;
}
int Mid = (L+R)>>1;
if(l<=Mid)dQuery(l,r,index<<1,L,Mid);
if(r>Mid)dQuery(l,r,index<<1|1,Mid+1,R);
}
int main(){
scanf("%d",&T);
int cnt = 0;
while(T--){
cnt++;
printf("Case %d:\n",cnt);
scanf("%d",&N);
BuildTree(1,N,1);
while(1){
cin >> Str;
if(Str=="Query"){
int l,r;
scanf("%d%d",&l,&r);
ans = 0;
dQuery(l,r,1,1,N);
printf("%d\n",ans);
}
else if(Str=="Add"){
int p,v;
scanf("%d%d",&p,&v);
pAdd(p,v,1,1,N);
}
else if(Str=="Sub"){
int p,v;
scanf("%d%d",&p,&v);
pAdd(p,-v,1,1,N);
}
else break;
}
}
return 0;
}
hdu1754——I Hate It(区间最值,RMQ同样适用(空间不爆的话))
代码:
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = 2e5+5;
int N,Q,Tree[maxn<<2],ans;
void BuildTree(int L,int R,int index){
if(L==R){
scanf("%d",&Tree[index]);
return;
}
int Mid = (L+R)>>1;
BuildTree(L,Mid,index<<1);
BuildTree(Mid+1,R,index<<1|1);
Tree[index] = max(Tree[index<<1],Tree[index<<1|1]);
}
void dQuery(int l,int r,int L,int R,int index){
if(l<=L&&R<=r){
ans = max(Tree[index],ans);
return;
}
int Mid = (L + R)>>1;
if(l<=Mid)dQuery(l,r,L,Mid,index<<1);
if(r>Mid)dQuery(l,r,Mid+1,R,index<<1|1);
}
void pChange(int p,int v,int L,int R,int index){
if(L==R){
Tree[index] = v;
return;
}
int Mid = (L+R)>>1;
if(p<=Mid)pChange(p,v,L,Mid,index<<1);
else pChange(p,v,Mid+1,R,index<<1|1);
Tree[index] = max(Tree[index<<1],Tree[index<<1|1]);
}
int main(){
while(~scanf("%d%d",&N,&Q)){
BuildTree(1,N,1);
for(int i = 1;i <= Q;i++){
string Str;
cin >> Str;
if(Str=="Q"){
int l,r;
ans = 0;
scanf("%d%d",&l,&r);
ans = 0;
dQuery(l,r,1,N,1);
printf("%d\n",ans);
}
else {
int p,v;
scanf("%d%d",&p,&v);
pChange(p,v,1,N,1);
}
}
}
return 0;
}
hdu1698——Just a Hook(区间修改,直接改掉的那种,维护整个区间的和,lazy标记不需要再查询时下传)
代码:
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = 1e5+5;
int N,M,Tree[maxn<<2];
int Tag[maxn<<2];
void BuildTree(int L,int R,int index){
Tag[index] = 0;
if(L==R){
Tree[index] = 1;
return;
}
int Mid = (L + R)>>1;
BuildTree(L,Mid,index<<1);
BuildTree(Mid+1,R,index<<1|1);
Tree[index] = Tree[index<<1] + Tree[index<<1|1];
}
void updata(int L,int R,int index){
int Mid = (L + R)>>1;
Tag[index<<1] = Tag[index];
Tag[index<<1|1] = Tag[index];
Tree[index<<1] = Tag[index]*(Mid - L + 1);
Tree[index<<1|1] = Tag[index]*(R - Mid);
Tag[index] = 0;
}
void dChange(int l,int r,int v,int L,int R,int index){
if(l<=L&&R<=r){
Tree[index] = (R - L + 1) * v;
Tag[index] = v;
return;
}
if(Tag[index])updata(L,R,index);
int Mid =(L + R)>>1;
if(l<=Mid)dChange(l,r,v,L,Mid,index<<1);
if(r>Mid)dChange(l,r,v,Mid+1,R,index<<1|1);
Tree[index] = Tree[index<<1] + Tree[index<<1|1];
}
int main(){
int T,cnt = 0;
scanf("%d",&T);
while(T--){
cnt++;
scanf("%d%d",&N,&M);
BuildTree(1,N,1);
for(int i = 1;i <= M;i++){
int l,r,v;
scanf("%d%d%d",&l,&r,&v);
dChange(l,r,v,1,N,1);
}
printf("Case %d: The total value of the hook is %d.\n",cnt,Tree[1]);
}
return 0;
}
POJ3468——A Simple Problem with Integers(区间修改,区间查询,查询时记得下传lazy标记)
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = 1e6+5;
typedef long long ll;
ll N,Opt,Tree[maxn<<2],Tag[maxn<<2],ans;
void BuildTree(int L,int R,int index){
if(L==R){
scanf("%lld",&Tree[index]);
return;
}
int Mid = (L + R)>>1;
BuildTree(L,Mid,index<<1);
BuildTree(Mid+1,R,index<<1|1);
Tree[index] = Tree[index<<1] + Tree[index<<1|1];
}
void Updata(int L,int R,int index){
int Mid = (L + R)>>1;
Tag[index<<1] += Tag[index];
Tag[index<<1|1] += Tag[index];
Tree[index<<1] += (Mid - L + 1)*Tag[index];
Tree[index<<1|1] += (R - Mid)*Tag[index];
Tag[index] = 0;
}
void dQuery(int l,int r,int L,int R,int index){
if(l<=L&&R<=r){
ans+=Tree[index];
return;
}
if(Tag[index])Updata(L,R,index);
int Mid = ( L + R )>>1;
if(l<=Mid)dQuery(l,r,L,Mid,index<<1);
if(Mid<r)dQuery(l,r,Mid+1,R,index<<1|1);
}
void dChange(int l,int r,int v,int L,int R,int index){
if(L>=l&&R<=r){
Tree[index] += (R - L + 1)*v;
Tag[index] += v;
return;
}
if(Tag[index])Updata(L,R,index);
int Mid = (L + R)>>1;
if(l<=Mid)dChange(l,r,v,L,Mid,index<<1);
if(r>Mid)dChange(l,r,v,Mid+1,R,index<<1|1);
Tree[index] = Tree[index<<1] + Tree[index<<1|1];
}
int main(){
scanf("%lld%lld",&N,&Opt);
BuildTree(1,N,1);
string str;
for(int i = 1;i <= Opt;i++){
cin >> str;
if(str=="Q"){
int l,r;
scanf("%d%d",&l,&r);
ans = 0;
dQuery(l,r,1,N,1);
printf("%lld\n",ans);
}
else{
int l,r,v;
scanf("%d%d%d",&l,&r,&v);
dChange(l,r,v,1,N,1);
}
}
return 0;
}
2019牛客多校训练营第四场C——sequence(单调栈确定每个数对应的最小值的区间,线段树维护区间最值,遍历每个数,算出每个数对应区间被改数数分为左右两个区间分别的最值的差值与该数的乘积)
讲解本题的博客(代码不同,不同时期写的嘛,大同小异,就是线段树的写法不同)
代码:
#include"bits/stdc++.h"
using namespace std;
typedef long long ll;
const int maxn = 3e6+5;
const ll INF = 1e18;
ll Tree1[maxn<<2],Tree2[maxn<<2],a[maxn],b[maxn],sum[maxn],S[maxn],cnt = 0,ans = -INF;
int N,L[maxn],R[maxn];
void BuildTree(int L,int R,int index){
if(L==R){
Tree1[index] = Tree2[index] = sum[++cnt];
return;
}
int Mid = (L + R)>>1;
BuildTree(L,Mid,index<<1);
BuildTree(Mid+1,R,index<<1|1);
Tree1[index] = max(Tree1[index<<1],Tree1[index<<1|1]);
Tree2[index] = min(Tree2[index<<1],Tree2[index<<1|1]);
}
ll Query1(int l,int r,int L,int R,int index){
if(l<=L&&R<=r){
return Tree1[index];
}
int Mid = (L + R)>>1;
ll ans = -INF;
if(l<=Mid)ans = max(ans,Query1(l,r,L,Mid,index<<1));
if(r>Mid)ans = max(ans,Query1(l,r,Mid + 1,R,index<<1|1));
return ans;
}
ll Query2(int l,int r,int L,int R,int index){
if(l<=L&&R<=r){
return Tree2[index];
}
int Mid = (L + R)>>1;
ll ans = INF;
if(l<=Mid)ans = min(ans,Query2(l,r,L,Mid,index<<1));
if(r>Mid)ans = min(ans,Query2(l,r,Mid + 1,R,index<<1|1));
return ans;
}
int main(){
scanf("%d",&N);
N++;
for(int i = 2;i <= N;i++){
scanf("%lld",&a[i]);
}
sum[1] = 0;
for(int i = 2;i <= N;i++){
scanf("%lld",&b[i]);
sum[i] = sum[i-1] + b[i];
}
int top = 0;//个人喜好,喜欢左右分别维护,可能测评姬抖的厉害的时候会卡住。
for(int i = 2;i <= N;i++){
while(top>0&&a[S[top]]>=a[i])top--;
if(top==0)L[i] = 2;
else L[i] = S[top] + 1;
S[++top] = i;
}
top = 0;
for(int i = N;i >= 2;i--){
while(top>0&&a[S[top]]>=a[i])top--;
if(top==0)R[i] = N;
else R[i] = S[top] - 1;
S[++top] = i;
}
BuildTree(1,N,1);
for(int i = 2;i<=N;i++){
ll p;
if(a[i]>0){
p = (Query1(i,R[i],1,N,1) - Query2(L[i] - 1,i - 1,1,N,1));
}
else
p = (Query2(i,R[i],1,N,1) - Query1(L[i] - 1,i - 1,1,N,1));
ans = max(ans,p*a[i]);
}
printf("%lld\n",ans);
return 0;
}