线段树进阶练习专题
小白逛公园
题目大意:
求一段区间里最大子段和
思路:
一段区间里的最大子段和分为以下几种情况:
1.当最大子段和是左儿子或者右儿子中一个的一部分时:
这段区间里的最大子段和为 左儿子的最大子段和 和 右儿子的最大子段和 的最大值。
2.当最大子段和中既有左儿子的一段又有右儿子的一段时:
容易发现如果出现这种情况,则这个最大值一定是 左儿子的最大后缀 和 右儿子的最大前缀 之和。
所以我们还需要维护每个区间的最大后缀和最大前缀
3.如何维护最大前缀(后缀):
发现最大前缀分为以下几种情况:
- 左儿子的最大前缀
- 左儿子的全部和右儿子的最大前缀
两者取最大值,最大后缀同理。
code:
#include<bits/stdc++.h> using namespace std; const int MAXN=500100; int m,n; int a[MAXN]; inline int read() { int x=0,f=1; char ch=getchar(); while(ch>'9'||ch<'0'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=x*10+ch-'0'; ch=getchar(); } return x*f; } struct node{ int l,r; long long sum;//区间和 long long maxl,maxr;//最大前缀和and最大后缀和 long long maxn;//最大子段和 node() { l=r=0; sum=maxl=maxr=maxn=0; } }z[MAXN*4]; node operator+(const node &l,const node &r) { node res; res.l=l.l;res.r=r.r; res.sum=l.sum+r.sum; res.maxl=max(l.maxl,l.sum+r.maxl); res.maxr=max(r.maxr,r.sum+l.maxr); res.maxn=max(max(l.maxn,r.maxn),l.maxr+r.maxl); return res; } void build(int l,int r,int rt) { if(l==r){ z[rt].l=z[rt].r=l; z[rt].sum=a[l]; z[rt].maxl=z[rt].maxr=z[rt].maxn=a[l];//子段中必须有元素 // cout<<z[rt].maxr<<endl; return; } int mid=(l+r)>>1; // cout<<mid<<endl; build(l,mid,rt<<1); build(mid+1,r,rt<<1|1); z[rt]=z[rt<<1]+z[rt<<1|1]; } void update(int l,int r,int rt,int p,int v)//单点修 { if(l==r){ z[rt].sum=z[rt].maxl=z[rt].maxn=z[rt].maxr=v; return; } int mid=(l+r)>>1; if(mid>=p) update(l,mid,rt<<1,p,v); else if(mid<p) update(mid+1,r,rt<<1|1,p,v); z[rt]=z[rt<<1]+z[rt<<1|1]; } node query(int l,int r,int rt,int nowl,int nowr) { if(l>=nowl&&r<=nowr){ return z[rt]; } int mid=(l+r)>>1; if(mid>=nowl){ if(mid<nowr) return query(l,mid,rt<<1,nowl,nowr)+query(mid+1,r,rt<<1|1,nowl,nowr); else return query(l,mid,rt<<1,nowl,nowr); } else return query(mid+1,r,rt<<1|1,nowl,nowr); } int main() { //cin>>n>>m; n=read();m=read(); for(int i=1;i<=n;i++) { //cin>>a[i]; a[i]=read(); } build(1,n,1); while(m--) { int k,p,s; k=read();p=read();s=read(); if(k==1)//选择 { if(p>s)swap(p,s); cout<<query(1,n,1,p,s).maxn<<endl; } else//单点修 update(1,n,1,p,s); } return 0; }
方差
题目大意:
求一段区间内的数的平均数和方差
思路:
1.求平均数
维护区间和,最后查询求一下平均数
2.求方差
把方差公式展开
所以我们还需要维护区间平方和
so 如何维护区间平方和呢?
字丑见谅qwq
code:
重要的事情说三遍:
(我才不会说我因为这个调了两天)
#include<bits/stdc++.h> using namespace std; const int maxn=100100; int m,n; double a[maxn]; struct node{ int l,r;//左右端点 double sum;//区间和 double sq_sum;//区间平方和 double tag;//加法标记 node(){ l=r=0; sum=sq_sum=tag=0; } }z[maxn*4]; node operator+(const node &l,const node &r) { node res; res.l=l.l;res.r=r.r; res.sum=l.sum+r.sum; res.sq_sum=l.sq_sum+r.sq_sum; return res; } void build(int l,int r,int rt) { if(l==r) { z[rt].l=z[rt].r=l; z[rt].sum=a[l]; z[rt].sq_sum=a[l]*a[l]; return; } int mid=(l+r)>>1; build(l,mid,rt<<1); build(mid+1,r,rt<<1|1); z[rt]=z[rt<<1]+z[rt<<1|1]; } void color(int rt,double k)//!!!!! { z[rt].sq_sum+=2*k*z[rt].sum+(z[rt].r-z[rt].l+1)*k*k; z[rt].sum+=(z[rt].r-z[rt].l+1)*k; z[rt].tag+=k;? } void push_col(int rt) { if(z[rt].tag) { color(rt<<1,z[rt].tag); color(rt<<1|1,z[rt].tag); z[rt].tag=0; } } void update(int l,int r,int rt,int nowl,int nowr,double k) { if(l>=nowl&&r<=nowr) { color(rt,k); return; } push_col(rt); int mid=(l+r)>>1; if(mid>=nowl) update(l,mid,rt<<1,nowl,nowr,k); if(mid<nowr) update(mid+1,r,rt<<1|1,nowl,nowr,k); z[rt]=z[rt<<1]+z[rt<<1|1]; } node query(int l,int r,int rt,int nowl,int nowr)//查找区间//!!! { if(l>=nowl&&r<=nowr)//当前区间包括在内 return z[rt]; push_col(rt); int mid=(l+r)>>1; if(mid>=nowl){ if(mid<nowr) return query(l,mid,rt<<1,nowl,nowr)+query(mid+1,r,rt<<1|1,nowl,nowr); else return query(l,mid,rt<<1,nowl,nowr); } else return query(mid+1,r,rt<<1|1,nowl,nowr); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%lf",&a[i]); } build(1,n,1); while(m--) { int opt,x,y; scanf("%d%d%d",&opt,&x,&y); if(opt==1){ double k; scanf("%lf",&k); update(1,n,1,x,y,k); } if(opt==2) { printf("%.4lf\n",query(1,n,1,x,y).sum/(y-x+1));//平均数包没问题的 } if(opt==3){//方差公式 double sum1=query(1,n,1,x,y).sum/(double)(y-x+1);//平均数 double sum2=query(1,n,1,x,y).sq_sum/(double)(y-x+1);//平方和 printf("%.4lf\n",(sum2-sum1*sum1)); } } return 0; }
降雨量
听说只有看过天气之子的人才能AC这道题(
题目大意:
询问“X年是自Y年以来降雨量最多的。”这句话是“必真”、“必假”还是“有可能”。
思路:
思路还是很好想的,重要的是判断的细节
- 先判
:
1、当右端点年份确定,且中间年份最大降雨量大于等于右端点降雨量
2、当左端点年份确定,且中间年份最大降雨量大于等于左端点降雨量
3、当左右端点年份都确定,且左端点降雨量小于等于右端点降雨量
- 再判
:
1、当左右端点之差不等于左右端点年份之差(等价于年份不连续,也就是我前面所说的更好的判断区间连续的方法)
2、左端点年份不确定
3、右端点年份不确定
(因为已经切掉
- 最后判断
:
若上面情况都不满足,那么肯定是
code
#include<bits/stdc++.h> using namespace std; const int minn=INT_MIN; const int maxn=50010; int m,n; int pre=0; struct num{ int year,v; bool vis; num(){year=0;v=minn;vis=false;} }a[100010]; struct node{ int maxv,minv; node() { maxv=minv=minn; } }z[400010]; node operator+(const node &l,const node &r) { node res; res.maxv=max(l.maxv,r.maxv); res.minv=min(l.minv,r.minv); return res; } int go(int x)//最大的year小于等于x的位置 { if(x<a[1].year)return 0; //找到x在离散化后的数 int l=1,r=2*n; while(l<r) { int mid=(l+r)>>1; if(a[mid].year<=x) l=mid+1; else r=mid; } return l-1; } void build(int l,int r,int rt) { if(l==r){ z[rt].maxv=z[rt].minv=a[l].v; // cout<<rt<<" "<<l<<" "<<r<<" "<<z[rt].maxv<<" "<<z[rt].minv<<endl; return; } int mid=(l+r)>>1; build(l,mid,rt<<1); build(mid+1,r,rt<<1|1); z[rt]=z[rt<<1]+z[rt<<1|1]; // cout<<rt<<" "<<l<<" "<<r<<" "<<z[rt].maxv<<" "<<z[rt].minv<<endl; } node query(int l,int r,int rt,int nowl,int nowr) { if(l>=nowl&&r<=nowr) { return z[rt]; } int mid=(l+r)>>1; if(mid>=nowl) { if(mid<nowr) return query(l,mid,rt<<1,nowl,nowr)+query(mid+1,r,rt<<1|1,nowl,nowr); else return query(l,mid,rt<<1,nowl,nowr); } else return query(mid+1,r,rt<<1|1,nowl,nowr); } int main() { // freopen("P2471_1.in","r",stdin); // freopen("P2471_1.out","w",stdout); cin>>n; int now=0; for(int i=1;i<=n;i++) { int year,v; cin>>year>>v; if(now+1!=year&&i!=1)//year和上一个之间还有年份 { pre++; } now=year; pre++; a[pre].v=v; a[pre].year=year; a[pre].vis=true; } for(int i=1;i<=n*2;i++) { if(!a[i].vis)//实际并没有这个节点 a[i].year=a[i-1].year+1; //离散化成功! } build(1,n*2,1); cin>>m; while(m--) { int x,y; cin>>y>>x; int yy=go(y); int xx=go(x); // cout<<xx<<" "<<yy<<endl; node res; if(xx==yy){ cout<<"maybe"<<endl; continue; } if(a[xx].v!=minn&&a[yy].v!=minn&&x==y+1&&a[xx].v<a[yy].v) { cout<<"true"<<endl; continue; } if(a[xx].v!=minn&&a[yy].v!=minn&&x==y+1&&a[xx].v>=a[yy].v) { cout<<"false"<<endl; continue; } if(xx==yy+1&&x!=y+1) { cout<<"maybe"<<endl; continue; } if(yy+1<=xx-1) res=query(1,n*2,1,yy+1,xx-1); if(xx==yy){ cout<<"maybe"<<endl; continue; } if((a[xx].v!=minn&&res.maxv>=a[xx].v)||(a[yy].v!=minn&&res.maxv>=a[yy].v)||(a[xx].v!=minn&&a[yy].v!=minn&&a[yy].v<=a[xx].v)) { cout<<"false"<<endl; continue; } else if((yy+1<=xx-1&&res.minv==minn)||a[xx].v==minn||a[yy].v==minn) { cout<<"maybe"<<endl; continue; } else cout<<"true"<<endl; } return 0; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库