这次的注释可能略显繁琐,但是是我认真写的帮助大家理解也帮助自己回顾,本文中重复部分不再添加注释,还请查看之前的部分作参考。
水果姐逛水果街Ⅰ
http://codevs.cn/problem/3304/
//线段树版本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
int n,m,a,b,tot;
int num[200010];
struct inte
{
int l,r;
ll maxn,minx,lans,rans;//区间最大值,最小值,区间内从左向右的答案,从右向左的答案
}tree[1000010];
void update(int now)
{
tree[now].maxn=max(tree[now<<1].maxn,tree[now<<1|1].maxn);
tree[now].minx=min(tree[now<<1].minx,tree[now<<1|1].minx);
ll tmp1=tree[now<<1|1].maxn-tree[now<<1].minx;
ll tmp2=tree[now<<1].maxn-tree[now<<1|1].minx;
tree[now].lans=max(tmp1,max(tree[now<<1].lans,tree[now<<1|1].lans));//右儿子的最大值-左儿子的最小值(跨越左右子区间从左向右),与左右儿子中的从左向右答案取max
tree[now].rans=max(tmp2,max(tree[now<<1].rans,tree[now<<1|1].rans));//左儿子的最大值-右儿子的最小值(跨越左右子区间从右向左),与左右儿子中的从右向左答案取max
}
void build(int now,int l,int r)
{
tree[now].l=l;
tree[now].r=r;
if(l==r)
{
tree[now].maxn=num[l];
tree[now].minx=num[l];
return;
}
int mid=(l+r)>>1;
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
update(now);
}
inte query(int now,int l,int r)//结构体类型,便于返回多个变量
{
if(tree[now].l>=l&&tree[now].r<=r)
return tree[now];
inte tmp,tmp1,tmp2;
tmp1=(inte){0,0,-1e9+7,1e9+7,0,0};//初始化
tmp2=(inte){0,0,-1e9+7,1e9+7,0,0};
int mid=(tree[now].l+tree[now].r)>>1;
if(l<=mid)
tmp1=query(now<<1,l,r);
if(r>mid)
tmp2=query(now<<1|1,l,r);
tmp.lans=max(tmp1.lans,max(tmp2.lans,tmp2.maxn-tmp1.minx));//左儿子递归的从左向右答案,右儿子递归的从左向右答案,与右儿子的最大值-左儿子的最小值取max
tmp.rans=max(tmp1.rans,max(tmp2.rans,tmp1.maxn-tmp2.minx));//左儿子递归的从右向左答案,右儿子递归的从右向左答案,与左儿子的最大值-右儿子的最小值取max
tmp.maxn=max(tmp1.maxn,tmp2.maxn);
tmp.minx=min(tmp1.minx,tmp2.minx);
return tmp;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&num[i]);
build(1,1,n);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
if(a<=b)
printf("%lld\n",query(1,a,b).lans);
else printf("%lld\n",query(1,b,a).rans);
}
return 0;
}
//ST表版本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int n,m,a,b,tot,ans;
int num[200010],lans[200010][20],rans[200010][20],maxs[200010][20],mins[200010][20];
void build()//预处理
{
for(int i=1;i<=n;i++)
maxs[i][0]=num[i],mins[i][0]=num[i];
for(int j=1;(1<<j)<=n;j++)
for(int i=1;i+(1<<j)-1<=n;i++)
{
maxs[i][j]=max(maxs[i][j-1],maxs[i+(1<<(j-1))][j-1]);//向后跳2^(j-1)步的最大值,向后跳2^j(向后跳2^(j-1)步再跳2^(j-1))步的最大值取max
mins[i][j]=min(mins[i][j-1],mins[i+(1<<(j-1))][j-1]);//向后跳2^(j-1)步的最小值,向后跳2^j步的最小值取min
lans[i][j]=max(maxs[i+(1<<(j-1))][j-1]-mins[i][j-1],max(lans[i][j-1],lans[i+(1<<(j-1))][j-1]));
//向后跳2^j步的最大值-向后跳2^(j-1)步的最小值,向后跳2^(j-1)步的从左向右的答案与向后跳2^j步的从左向右的答案取max
rans[i][j]=max(maxs[i][j-1]-mins[i+(1<<(j-1))][j-1],max(rans[i+(1<<(j-1))][j-1],rans[i][j-1]));
//向后跳2^(j-1)步的最大值-向后跳2^j步的最小值,向后跳2^j步的从右向左的答案与向后跳2^(j-1)步的从右向左的答案取max
}
}
int query_1(int l,int r)
{
int k=0,ans=0,minx=1e9+7;
while((1<<(k+1))<=r-l+1)
k++;
for(int i=k;i>=0;i--)
{
if(l+(1<<i)-1<=r)
{
ans=max(ans,max(lans[l][i],maxs[l][i]-minx));//l向后跳2^i步的从左向右的答案,l向后跳2^i步的最大值-上一个位置的l(未向后跳2^(i+1)步)向后跳2^(i+1)步的最小值
minx=min(minx,mins[l][i]); //即此次跳跃区间的最大值-上一个跳跃区间的最小值
l+=(1<<i);
}
}
return ans;
}
int query_2(int l,int r)
{
int k=0,ans=0,maxn=-1e9+7;
while((1<<(k+1))<=r-l+1)
k++;
for(int i=k;i>=0;i--)
{
if(l+(1<<i)-1<=r)
{
ans=max(ans,max(rans[l][i],maxn-mins[l][i]));//l向后跳2^i步的从右向左的答案,上一个位置的l(未向后跳2^(i+1)步)向后跳2^(i+1)步的最大值-l向后跳2^i步的最小值
maxn=max(maxn,maxs[l][i]); //即上一个跳跃区间的最大值-此次跳跃区间的最小值
l+=(1<<i);
}
}
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&num[i]);
build();
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
ans=0;
scanf("%d%d",&a,&b);
if(a<=b)
printf("%d\n",query_1(a,b));
else
printf("%d\n",query_2(b,a));
}
return 0;
}
水果姐逛水果街Ⅱ
http://codevs.cn/problem/3305/
//线段树版本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
int n,m,tot,ru,rv,a,b;
int num[200010],first[400010],nxt[400010],deep[200010],top[200010],f[200010],siz[200010],son[200010],toseg[200010],totre[200010];
struct edge
{
int u,v;
}l[400010];
struct inte
{
int l,r;
ll lans,rans,maxn,minx;
}tree[1000010];
void add(int f,int t)
{
l[++tot]=(edge){f,t};
nxt[tot]=first[f];
first[f]=tot;
}
void dfs_1(int k,int fa,int d)//树链剖分
{
deep[k]=d;
f[k]=fa;
siz[k]=1;
for(int i=first[k];i!=-1;i=nxt[i])
{
int x=l[i].v;
if(x==fa)
continue;
dfs_1(x,k,d+1);
siz[k]+=siz[x];
if(!son[k]||siz[x]>siz[son[k]])
son[k]=x;
}
}
void dfs_2(int k,int num)
{
top[k]=num;
toseg[k]=++tot;
totre[toseg[k]]=k;
if(!son[k])
return;
dfs_2(son[k],num);
for(int i=first[k];i!=-1;i=nxt[i])
{
int x=l[i].v;
if(x!=son[k]&&x!=f[k])
dfs_2(x,x);
}
}
void update(int now)//参考水果姐I理解
{
tree[now].maxn=max(tree[now<<1].maxn,tree[now<<1|1].maxn);
tree[now].minx=min(tree[now<<1].minx,tree[now<<1|1].minx);
tree[now].lans=max(max(tree[now<<1].lans,tree[now<<1|1].lans),tree[now<<1|1].maxn-tree[now<<1].minx);
tree[now].rans=max(max(tree[now<<1].rans,tree[now<<1|1].rans),tree[now<<1].maxn-tree[now<<1|1].minx);
}
void build(int now,int l,int r)
{
tree[now].l=l;
tree[now].r=r;
if(l==r)
{
tree[now].maxn=num[totre[l]];
tree[now].minx=num[totre[l]];
return;
}
int mid=(l+r)>>1;
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
update(now);
}
inte ask(int now,int l,int r)
{
if(tree[now].l>=l&&tree[now].r<=r)
{
return tree[now];
}
inte tmp,tmp1,tmp2;
tmp1=(inte){0,0,-1e9+7,-1e9+7,-1e9+7,1e9+7};
tmp2=(inte){0,0,-1e9+7,-1e9+7,-1e9+7,1e9+7};
int mid=(tree[now].l+tree[now].r)>>1;
if(l<=mid)
tmp1=ask(now<<1,l,r);
if(r>mid)
tmp2=ask(now<<1|1,l,r);
tmp.lans=max(tmp1.lans,max(tmp2.lans,tmp2.maxn-tmp1.minx));
tmp.rans=max(tmp1.rans,max(tmp2.rans,tmp1.maxn-tmp2.minx));
tmp.maxn=max(tmp1.maxn,tmp2.maxn);
tmp.minx=min(tmp1.minx,tmp2.minx);
return tmp;
}
ll find_ans(int x,int y)//转换器
{
if(toseg[x]>toseg[y])//若x在线段树上的编号>y在线段树上的编号,即x在原树上的深度>y在原树上的深度
return ask(1,toseg[y],toseg[x]).rans;//返回原树从y向x走的线段树从右向左的答案
else return ask(1,toseg[x],toseg[y]).lans;//返回原树从x向y走的线段树从左向右的答案
}
ll find_max(int x,int y)
{
if(toseg[x]>toseg[y])
return ask(1,toseg[y],toseg[x]).maxn;
else return ask(1,toseg[x],toseg[y]).maxn;
}
ll find_min(int x,int y)
{
if(toseg[x]>toseg[y])
return ask(1,toseg[y],toseg[x]).minx;
else return ask(1,toseg[x],toseg[y]).minx;
}
ll query(int x,int y)//这里是由x向y走
{
ll ans=-1e9+7,maxn=-1e9+7,minx=1e9+7;
while(top[x]!=top[y])
{
if(deep[top[x]]>deep[top[y]])
{
ans=max(ans,find_ans(x,top[x]));
minx=min(minx,find_min(x,top[x]));
ans=max(ans,maxn-minx);//从y向lca跳经过区间的最大值-从x向lca跳经过区间内的最小值
x=f[top[x]];
}
else
{
ans=max(ans,find_ans(top[y],y));
maxn=max(maxn,find_max(top[y],y));
ans=max(ans,maxn-minx);
y=f[top[y]];
}
}
//注意x与y位置已经被更新至同一条重链上
ans=max(ans,find_ans(x,y));//x与y在同一条重链上时,x-y区间内部的答案
ans=max(ans,max(find_max(x,y)-minx,maxn-find_min(x,y)));//x-y区间内最大值-向上跳至同一条重链时从x向lca跳经过区间内的最小值,与向上跳至同一条重链时从y向lca跳经过区间内的最大值-x-y区间内最小值取max
ans=max(ans,maxn-minx);//向上跳至同一条重链时从y向lca跳经过区间内的最大值-从x向lca跳经过区间内的最小值
return ans;
}
int main()
{
memset(first,-1,sizeof(first));
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&num[i]);
for(int i=1;i<n;i++)
{
scanf("%d%d",&ru,&rv);//树中为双向边
add(ru,rv);
add(rv,ru);
}
tot=0;
dfs_1(1,0,1);
dfs_2(1,1);
build(1,1,n);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
printf("%lld\n",query(a,b));
}
return 0;
}
//ST表版本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int n,m,ru,rv,a,b,lca,tot;
int num[200010],first[400010],nxt[400010],deep[200010],anc[200010][21],lans[200010][21],rans[200010][21],maxs[200010][21],mins[200010][21];
struct edge
{
int u,v;
}l[400010];
void add(int f,int t)
{
l[++tot]=(edge){f,t};
nxt[tot]=first[f];
first[f]=tot;
}
void dfs(int k,int fa)
{
deep[k]=deep[fa]+1;
anc[k][0]=fa;//k向上跳2^0(1)步的祖先为fa
maxs[k][0]=max(num[k],num[fa]);//k向上跳2^0步的最大值
mins[k][0]=min(num[k],num[fa]);//k向上跳2^0步的最小值
lans[k][0]=max(num[fa]-num[k],0);//k向上跳2^0步的从下向上的答案
rans[k][0]=max(num[k]-num[fa],0);//k向上跳2^0步的从上向下的答案
for(int i=1;anc[k][i-1];i++)
{
anc[k][i]=anc[anc[k][i-1]][i-1];
maxs[k][i]=max(maxs[k][i-1],maxs[anc[k][i-1]][i-1]);
mins[k][i]=min(mins[k][i-1],mins[anc[k][i-1]][i-1]);
lans[k][i]=max(maxs[anc[k][i-1]][i-1]-mins[k][i-1],max(lans[k][i-1],lans[anc[k][i-1]][i-1]));
rans[k][i]=max(maxs[k][i-1]-mins[anc[k][i-1]][i-1],max(rans[k][i-1],rans[anc[k][i-1]][i-1]));
}
/*for(int i=1;anc[k][i-1];i++)//不同的预处理方式
{
anc[k][i]=anc[anc[k][i-1]][i-1];
}
for(int j=1;(1<<j)<=deep[k]-1;j++)
{
maxs[k][j]=max(maxs[k][j-1],maxs[anc[k][j-1]][j-1]);
mins[k][j]=min(mins[k][j-1],mins[anc[k][j-1]][j-1]);
lans[k][j]=max(maxs[anc[k][j-1]][j-1]-mins[k][j-1],max(lans[k][j-1],lans[anc[k][j-1]][j-1]));
rans[k][j]=max(maxs[k][j-1]-mins[anc[k][j-1]][j-1],max(rans[k][j-1],rans[anc[k][j-1]][j-1]));
}*/
for(int i=first[k];i!=-1;i=nxt[i])
{
int x=l[i].v;
if(!deep[x])
{
dfs(x,k);
}
}
}
int count(int x,int y)
{
int k=0,ans=0,minx=1e9+7,maxn=-1e9+7;
int d=deep[x]-deep[lca];
if(d>0)
{
for(int i=20;i>=0;i--)
{
if(d&(1<<i))
{
ans=max(ans,max(lans[x][i],maxs[x][i]-minx));
minx=min(minx,mins[x][i]);
x=anc[x][i];
}
}
}
d=deep[y]-deep[lca];
if(d>0)
{
for(int i=20;i>=0;i--)
{
if(d&(1<<i))
{
ans=max(ans,max(rans[y][i],maxn-mins[y][i]));
maxn=max(maxn,maxs[y][i]);
y=anc[y][i];
}
}
}
return max(ans,maxn-minx);
}
/*int count(int x,int y)//不同的统计方式
{
int k=0,ans=0,minx=1e9+7,maxn=-1e9+7;
if(deep[x]>deep[lca])
{
int l=1,r=deep[x]-deep[lca]+1;
while((1<<(k+1))<=r-l)
k++;
for(int i=k;i>=0;i--)
{
if(l+(1<<i)<=r)
{
ans=max(ans,max(lans[x][i],maxs[x][i]-minx));
minx=min(minx,mins[x][i]);
x=anc[x][i];
l+=(1<<i);
}
}
}
if(deep[y]>deep[lca])
{
k=0;
int l=1,r=deep[y]-deep[lca]+1;
while((1<<(k+1))<=r-l)
k++;
for(int i=k;i>=0;i--)
{
if(l+(1<<i)<=r)
{
ans=max(ans,max(rans[y][i],maxn-mins[y][i]));
maxn=max(maxn,maxs[y][i]);
y=anc[y][i];
l+=(1<<i);
}
}
}
return max(ans,maxn-minx);
}*/
int ask_lca(int x,int y)//倍增lca
{
if(deep[x]<deep[y])
swap(x,y);
if(deep[x]>deep[y])
{
int d=deep[x]-deep[y];
for(int i=0;i<=20;i++)
{
if(d&(1<<i))
x=anc[x][i];
}
}
if(x!=y)
{
for(int i=20;i>=0;i--)
{
if(anc[x][i]!=anc[y][i])
{
x=anc[x][i];
y=anc[y][i];
}
}
}
if(x==y)
lca=x;
else lca=anc[x][0];
return count(a,b);
}
int main()
{
memset(first,-1,sizeof(first));
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&num[i]);
for(int i=1;i<n;i++)
{
scanf("%d%d",&ru,&rv);
add(ru,rv);
add(rv,ru);
}
tot=0;
dfs(1,0);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
tot=0;
scanf("%d%d",&a,&b);
printf("%d\n",ask_lca(a,b));
}
return 0;
}
水果姐逛水果街Ⅲ
http://codevs.cn/problem/3306/
这个好像只能用线段树做了…
主体与II完全相同,只需要加个point_set就好啦.
//线段树版本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
int n,m,tot,ru,rv,a,b,p;
int num[200010],first[400010],nxt[400010],deep[200010],top[200010],f[200010],siz[200010],son[200010],toseg[200010],totre[200010];
struct edge
{
int u,v;
}l[400010];
struct inte
{
int l,r;
ll lans,rans,maxn,minx;
}tree[1000010];
void add(int f,int t)
{
l[++tot]=(edge){f,t};
nxt[tot]=first[f];
first[f]=tot;
}
void dfs_1(int k,int fa,int d)
{
deep[k]=d;
f[k]=fa;
siz[k]=1;
for(int i=first[k];i!=-1;i=nxt[i])
{
int x=l[i].v;
if(x==fa)
continue;
dfs_1(x,k,d+1);
siz[k]+=siz[x];
if(!son[k]||siz[x]>siz[son[k]])
son[k]=x;
}
}
void dfs_2(int k,int num)
{
top[k]=num;
toseg[k]=++tot;
totre[toseg[k]]=k;
if(!son[k])
return;
dfs_2(son[k],num);
for(int i=first[k];i!=-1;i=nxt[i])
{
int x=l[i].v;
if(x!=son[k]&&x!=f[k])
dfs_2(x,x);
}
}
void update(int now)//参考水果姐2理解
{
tree[now].maxn=max(tree[now<<1].maxn,tree[now<<1|1].maxn);
tree[now].minx=min(tree[now<<1].minx,tree[now<<1|1].minx);
tree[now].lans=max(max(tree[now<<1].lans,tree[now<<1|1].lans),tree[now<<1|1].maxn-tree[now<<1].minx);
tree[now].rans=max(max(tree[now<<1].rans,tree[now<<1|1].rans),tree[now<<1].maxn-tree[now<<1|1].minx);
}
void build(int now,int l,int r)
{
tree[now].l=l;
tree[now].r=r;
if(l==r)
{
tree[now].maxn=num[totre[l]];
tree[now].minx=num[totre[l]];
return;
}
int mid=(l+r)>>1;
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
update(now);
}
void point_set(int now,int pos,ll value)
{
if(tree[now].l==tree[now].r)
{
tree[now].maxn=value;
tree[now].minx=value;
return;
}
int mid=(tree[now].l+tree[now].r)>>1;
if(pos<=mid)
point_set(now<<1,pos,value);
else
point_set(now<<1|1,pos,value);
update(now);
}
inte ask(int now,int l,int r)
{
if(tree[now].l>=l&&tree[now].r<=r)
{
return tree[now];
}
inte tmp,tmp1,tmp2;
tmp1=(inte){0,0,-1e9+7,-1e9+7,-1e9+7,1e9+7};
tmp2=(inte){0,0,-1e9+7,-1e9+7,-1e9+7,1e9+7};
int mid=(tree[now].l+tree[now].r)>>1;
if(l<=mid)
tmp1=ask(now<<1,l,r);
if(r>mid)
tmp2=ask(now<<1|1,l,r);
tmp.lans=max(tmp1.lans,max(tmp2.lans,tmp2.maxn-tmp1.minx));
tmp.rans=max(tmp1.rans,max(tmp2.rans,tmp1.maxn-tmp2.minx));
tmp.maxn=max(tmp1.maxn,tmp2.maxn);
tmp.minx=min(tmp1.minx,tmp2.minx);
return tmp;
}
ll find_ans(int x,int y)
{
if(toseg[x]>toseg[y])
return ask(1,toseg[y],toseg[x]).rans;
else return ask(1,toseg[x],toseg[y]).lans;
}
ll find_max(int x,int y)
{
if(toseg[x]>toseg[y])
return ask(1,toseg[y],toseg[x]).maxn;
else return ask(1,toseg[x],toseg[y]).maxn;
}
ll find_min(int x,int y)
{
if(toseg[x]>toseg[y])
return ask(1,toseg[y],toseg[x]).minx;
else return ask(1,toseg[x],toseg[y]).minx;
}
ll query(int x,int y)
{
ll ans=-1e9+7,maxn=-1e9+7,minx=1e9+7;
while(top[x]!=top[y])
{
if(deep[top[x]]>deep[top[y]])
{
ans=max(ans,find_ans(x,top[x]));
minx=min(minx,find_min(x,top[x]));
ans=max(ans,maxn-minx);
x=f[top[x]];
}
else
{
ans=max(ans,find_ans(top[y],y));
maxn=max(maxn,find_max(top[y],y));
ans=max(ans,maxn-minx);
y=f[top[y]];
}
}
ans=max(ans,find_ans(x,y));
ans=max(ans,max(find_max(x,y)-minx,maxn-find_min(x,y)));
ans=max(ans,maxn-minx);
return ans;
}
int main()
{
memset(first,-1,sizeof(first));
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&num[i]);
for(int i=1;i<n;i++)
{
scanf("%d%d",&ru,&rv);
add(ru,rv);
add(rv,ru);
}
tot=0;
dfs_1(1,0,1);
dfs_2(1,1);
build(1,1,n);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&p,&a,&b);
if(p==0)
point_set(1,toseg[a],b);
if(p==1)
printf("%lld\n",query(a,b));
}
return 0;
}