线段树进阶练习专题

小白逛公园

题目大意:

求一段区间里最大子段和

思路:

一段区间里的最大子段和分为以下几种情况:

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:

重要的事情说三遍:
n,m,l,r
n,m,l,r
n,m,l,r
(我才不会说我因为这个调了两天)

#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年以来降雨量最多的。”这句话是“必真”、“必假”还是“有可能”。

思路:

思路还是很好想的,重要的是判断的细节

  • 先判false

      1、当右端点年份确定,且中间年份最大降雨量大于等于右端点降雨量

      2、当左端点年份确定,且中间年份最大降雨量大于等于左端点降雨量

      3、当左右端点年份都确定,且左端点降雨量小于等于右端点降雨量

  • 再判maybe:

      1、当左右端点之差不等于左右端点年份之差(等价于年份不连续,也就是我前面所说的更好的判断区间连续的方法)

      2、左端点年份不确定

      3、右端点年份不确定

      (因为已经切掉false的情况了,那么剩下的情况中可以直接照上面的判断!)

  • 最后判断true:
    若上面情况都不满足,那么肯定是true

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;
}

后记:

posted @   lazy_ZJY  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示