目录
给出序列,查询区间的最大子段和。
- 维护最大前缀子段和,最大后缀子段和,区间答案即可
- 区间合并的时候,大区间的答案只能是左区间答案,右区间答案,左后缀+右前缀三者最大值
- 查询过程可以返回node,重载 + 来合并答案
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define re register
#define lc rt<<1
#define rc rt<<1|1
const int maxn = 5e5+10;
const int mx = 40;
const ll mod = 998244353;
const ll inf = 34359738370;
const int INF = 1e9+7;
const double pi = acos(-1.0);
int n,m;
int a[maxn];
int sum[maxn];
struct node
{
int l,r;
int ans,pre,suf;
node operator + (const node &f)const
{
node t;
t.ans=max(max(ans,f.ans),suf+f.pre);
t.pre=max(pre,sum[r]-sum[l-1]+f.pre);
t.suf=max(f.suf,suf+sum[f.r]-sum[f.l-1]);
t.l=l,t.r=f.r;
return t;
}
}tree[maxn<<2];
inline void pushup(int rt)
{
tree[rt]=tree[lc]+tree[rc];
}
inline void build(int rt,int l,int r)
{
tree[rt].l=l,tree[rt].r=r;
if(l == r)
{
int d=a[l];
tree[rt].pre=tree[rt].suf=tree[rt].ans=d;
return ;
}
int mid=l+r>>1;
build(lc,l,mid);
build(rc,mid+1,r);
pushup(rt);
}
inline node query(int rt,int vl,int vr)
{
int l=tree[rt].l,r=tree[rt].r;
if(vl<=l && r<=vr) return tree[rt];
int mid=l+r>>1;
if(vr<=mid) return query(lc,vl,vr);
else if(vl > mid) return query(rc,vl,vr);
return query(lc,vl,vr)+query(rc,vl,vr);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",a+i);
for(int i=1;i<=n;i++) sum[i] = a[i]+sum[i-1];
build(1,1,n);
scanf("%d",&m);
while(m--)
{
int l,r;
scanf("%d %d",&l,&r);
printf("%d\n",query(1,l,r).ans);
}
return 0;
}
和1类似,只不过多了个单点修改的操作。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define re register
#define lc rt<<1
#define rc rt<<1|1
const int maxn = 5e4+10;
const int mx = 40;
const ll mod = 998244353;
const ll inf = 34359738370;
const int INF = 1e9+7;
const double pi = acos(-1.0);
struct node
{
ll ans,pre,suf,sum;
int l,r;
node operator + (const node &f)const
{
node t;
t.ans=max(suf+f.pre,max(ans,f.ans));
t.pre=max(pre,sum+f.pre);
t.suf=max(f.suf,f.sum+suf);
t.sum=sum+f.sum;
t.l=l,t.r=f.r;
return t;
}
}tree[maxn<<2];
inline void pushup(int rt)
{
tree[rt]=tree[lc]+tree[rc];
}
void build(int rt,int l,int r)
{
tree[rt].l=l,tree[rt].r=r;
if(l == r)
{
scanf("%lld",&tree[rt].ans);
tree[rt].pre=tree[rt].suf=tree[rt].sum=tree[rt].ans;
return ;
}
int mid=l+r>>1;
build(lc,l,mid);
build(rc,mid+1,r);
pushup(rt);
}
inline void upd(int rt,int pos,int v)
{
int l=tree[rt].l,r=tree[rt].r;
if(l==r)
{
tree[rt].sum=tree[rt].ans=v;
tree[rt].pre=tree[rt].suf=v;
return ;
}
int mid=l+r>>1;
if(pos<=mid) upd(lc,pos,v);
else upd(rc,pos,v);
pushup(rt);
}
inline node query(int rt,int vl,int vr)
{
int l=tree[rt].l,r=tree[rt].r;
if(vl<=l && r<=vr) return tree[rt];
int mid=l+r>>1;
if(vr<=mid) return query(lc,vl,vr);
else if(vl>mid) return query(rc,vl,vr);
return query(lc,vl,vr)+query(rc,vl,vr);
}
int n,m;
int main()
{
scanf("%d",&n);
build(1,1,n);
scanf("%d",&m);
while(m--)
{
int f,x,y;
scanf("%d %d %d",&f,&x,&y);
if(!f)
{
upd(1,x,y);
}
else printf("%lld\n",query(1,x,y).ans);
}
return 0;
}
- 区间每个数开根号,经典的单点暴力修改,每个点只能被修改log次。
- 记录最大值,如果最大值小于等于1,剪枝。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define re register
#define lc rt<<1
#define rc rt<<1|1
const int maxn = 1e5+10;
const int mx = 40;
const ll mod = 998244353;
const ll inf = 34359738370;
const int INF = 1e9+7;
const double pi = acos(-1.0);
struct node
{
int l,r;
ll mmax,sum;
node operator + (const node &f)const
{
node t;
t.mmax=max(mmax,f.mmax);
t.sum=sum+f.sum;
t.l=l,t.r=f.r;
return t;
}
}tree[maxn<<2];
inline void pushup(int rt)
{
tree[rt]=tree[lc]+tree[rc];
}
inline void build(int rt,int l ,int r)
{
tree[rt].l=l,tree[rt].r=r;
if(l==r)
{
scanf("%lld",&tree[rt].sum);
tree[rt].mmax=tree[rt].sum;
return ;
}
int mid=l+r>>1;
build(lc,l,mid);build(rc,mid+1,r);
pushup(rt);
}
inline void upd(int rt,int vl,int vr)
{
int l=tree[rt].l,r=tree[rt].r;
if(tree[rt].mmax <= 1 || l > vr || r<vl) return;
if(l == r)
{
ll d=(ll)sqrt(tree[rt].mmax);
tree[rt].mmax=tree[rt].sum=d;
return ;
}
int mid=l+r>>1;
upd(lc,vl,vr);
upd(rc,vl,vr);
pushup(rt);
}
inline node query(int rt,int vl,int vr)
{
int l=tree[rt].l,r=tree[rt].r;
if(vl<=l && r<=vr) return tree[rt];
int mid=l+r>>1;
if(vr<=mid) return query(lc,vl,vr);
else if(vl>mid) return query(rc,vl,vr);
return query(lc,vl,vr)+query(rc,vl,vr);
}
int n,cnt,m;
int main()
{
while(~scanf("%d",&n))
{
build(1,1,n);
scanf("%d",&m);
printf("Case #%d:\n",++cnt);
while(m--)
{
int f,x,y;
scanf("%d %d %d",&f,&x,&y);
if(x>y) swap(x,y);
if(!f)
{
upd(1,x,y);
}
else printf("%lld\n",query(1,x,y).sum);
}
puts("");
}
return 0;
}
-
这题咋一看很麻烦,但如果能从分类讨论的方向着手,会发现其实就是在第一题的基础上加了点东西。我们维护的信息和第一题一样。 -
首先给出的[x1,y1] ,[x2,y2] 只有两种情况,①y1<=x2,也就是两个区间中间隔了至少一个数 ②y1>x2 ,两个区间有相交 -
对于①,只有一种情况 我们发现无论左右端点怎么选,[y1,x2]这段是必选的,所以答案就是[y1,x2]的区间和加上max(0,[x1,y1-1]的后缀答案) ,再加上max(0,[x2+1,y2]的前缀答案)
对于②: 我们有4种情况
- (1) 的情况就是左右端点都在中间,所选的区间即为
[x2,y1] 的子区间,所以答案就是[x2,y1]的ans - (2) 的情况就是左端点在
[x1,x2] ,右端点在[x2,y1] ,x2这个点必取,然后加[x1,x2-1]的后缀,[x2+1,y1]的前缀 - (3)的情况和(2)对称的
- (4)左端点在[x1,x2] 右端点在[y1,y2], 中间段的区间和必取,然后加上[x1,x2-1]的后缀,[y1+1,y2]的前缀
- 4种情况取max即可,注意区间查询前要判断
l<=r
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define lc rt<<1
#define rc rt<<1|1
const int maxn = 1e4+10;
const int mx = 40;
const ll mod = 998244353;
const ll inf = 34359738370;
const int INF = 1e9+7;
const double pi = acos(-1.0);
struct node
{
int l,r;
int sum,ans,pre,suf;
node operator + (const node &f)const
{
node t;
t.l=l,t.r=f.r;
t.ans=max(suf+f.pre,max(ans,f.ans));
t.sum=sum+f.sum;
t.pre=max(pre,sum+f.pre);
t.suf=max(f.suf,f.sum+suf);
return t;
}
}tree[maxn<<2];
inline void pushup(int rt)
{
tree[rt]=tree[lc]+tree[rc];
}
inline void build(int rt,int l,int r)
{
tree[rt].l=l,tree[rt].r=r;
if(l == r)
{
scanf("%d",&tree[rt].ans);
tree[rt].sum=tree[rt].pre=tree[rt].suf=tree[rt].ans;
return ;
}
int mid=l+r>>1;
build(lc,l,mid);build(rc,mid+1,r);
pushup(rt);
}
inline node query(int rt,int vl,int vr)
{
int l=tree[rt].l,r=tree[rt].r;
if(vl<=l && r<=vr) return tree[rt];
int mid=l+r>>1;
if(vr<=mid) return query(lc,vl,vr);
else if(vl>mid) return query(rc,vl,vr);
return query(lc,vl,vr)+query(rc,vl,vr);
}
int n;
int main()
{
int t;cin>>t;
while(t--)
{
scanf("%d",&n);
build(1,1,n);
int m;scanf("%d",&m);
while(m--)
{
int x1,y1,x2,y2;
scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
if(y1<=x2)
{
int ans=query(1,y1,x2).sum;
if(x1<=y1-1)
ans=max(ans,ans+query(1,x1,y1-1).suf);
if(x2+1<=y2)
ans=max(ans,ans+query(1,x2+1,y2).pre);
printf("%d\n",ans);
}
else
{
int ans1=query(1,x2,y1).ans;
int ans2=query(1,x2,x2).sum;
if(x1<=x2-1)
ans2=max(ans2,ans2+query(1,x1,x2-1).suf);
if(x2+1<=y1)
ans2=max(ans2,ans2+query(1,x2+1,y1).pre);
int ans3=query(1,y1,y1).sum;
if(x2<=y1-1)
ans3=max(ans3,ans3+query(1,x2,y1-1).suf);
if(y1+1<=y2)
ans3=max(ans3,ans3+query(1,y1+1,y2).pre);
int ans4=query(1,x2,y1).sum;
if(x1<=x2-1)
ans4=max(ans4,ans4+query(1,x1,x2-1).suf);
if(y1+1<=y2)
ans4=max(ans4,ans4+query(1,y1+1,y2).pre);
printf("%d\n",max(max(ans1,ans2),max(ans3,ans4)));
}
}
}
return 0;
}
刚开始以为只是把第一题加了区间修改并且搬到树上,导致在树剖查询跳链的时候没有注意区间合并的方向性,一直wa…
- 线段树区间合并的时候,是左区间的pre和右区间的suf合并出新的ans
- 对于查询
[X,Y] ,我们不妨把LCA->X 这条链记为左链,LCA->Y 记为右链,我们发现,左链对应在dfs序中是几段不相连的子段,右链同样如此,所以我们需要左右链各自跳链,独立记录答案,最后在LCA处合并答案 。 - 此外有一个要注意的是,左链在合并的时候是把深度小的顶点作为左边,深度大的作为右边 , 然而我们查询X->Y是把深度大的作为左边,所以需要交换一下左链的pre和suf ,再进行最终的合并。
- 链的修改就是寻常的跳链。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define re register
const int maxn = 1e5+10;
const int mx = 40;
const ll mod = 998244353;
const ll inf = 34359738370;
const int INF = 1e9+7;
const double pi = acos(-1.0);
struct
{
int to,next;
}e[maxn<<1];
int head[maxn],cnt=0;
inline void add(int v,int u)
{
e[++cnt].to=u;
e[cnt].next=head[v];
head[v]=cnt;
}
int dep[maxn],fa[maxn],son[maxn],siz[maxn];
void dfs1(int rt,int par)
{
siz[rt]=1;
for(int i=head[rt];i;i=e[i].next)
{
int to=e[i].to;
if(to==par) continue;
fa[to]=rt;
dep[to]=dep[rt]+1;
dfs1(to,rt);
siz[rt]+=siz[to];
if(siz[to] > siz[son[rt]]) son[rt]=to;
}
}
int top[maxn],dfsc,in[maxn],pos[maxn];
void dfs2(int rt,int t)
{
top[rt]=t;
in[rt]=++dfsc;
pos[dfsc]=rt;
if(son[rt]) dfs2(son[rt],t);
for(int i=head[rt];i;i=e[i].next)
{
int to=e[i].to;
if(to==fa[rt] || to==son[rt]) continue;
dfs2(to,to);
}
}
int a[maxn],n;
struct node
{
#define lc rt<<1
#define rc rt<<1|1
int l,r;
int ans,pre,suf,sum;
int tag;
node operator + (const node &f)const
{
node t;
t.l=l,t.r=f.r;
t.sum=sum+f.sum;
t.ans=max(max(ans,f.ans),suf+f.pre);
t.pre=max(pre,sum+f.pre);
t.suf=max(f.suf,f.sum+suf);
t.tag=maxn;
return t;
}
}tree[maxn<<2];
inline void pushup(int rt)
{
tree[rt]=tree[lc]+tree[rc];
}
inline void build(int rt,int l,int r)
{
tree[rt].l=l,tree[rt].r=r;
tree[rt].tag=maxn;
if(l == r)
{
tree[rt].pre=tree[rt].suf=tree[rt].ans=a[pos[l]];
tree[rt].sum=a[pos[l]];
return ;
}
int mid=l+r>>1;
build(lc,l,mid);build(rc,mid+1,r);
pushup(rt);
}
inline void change(int rt,int len,int c)
{
tree[rt].pre=tree[rt].suf=tree[rt].ans=max(c,c*len);
tree[rt].sum=c*len;
tree[rt].tag=c;
}
inline void pushdown(int rt)
{
if(tree[rt].tag != maxn)
{
int l=tree[rt].l,r=tree[rt].r;
int mid=(l+r)>>1;
change(lc,mid-l+1,tree[rt].tag);
change(rc,r-mid,tree[rt].tag);
tree[rt].tag=maxn;
}
}
inline void upd(int rt,int vl,int vr,int c)
{
int l=tree[rt].l,r=tree[rt].r;
if(l>vr || r<vl) return ;
if(vl<=l && r<=vr)
{
change(rt,r-l+1,c);
return ;
}
int mid=l+r>>1;
pushdown(rt);
upd(lc,vl,vr,c);
upd(rc,vl,vr,c);
pushup(rt);
}
inline node query(int rt,int vl,int vr)
{
int l=tree[rt].l,r=tree[rt].r;
if(vl<=l && r<=vr) return tree[rt];
int mid=l+r>>1;
pushdown(rt);
if(vr<=mid) return query(lc,vl,vr);
else if(vl>mid) return query(rc,vl,vr);
return query(lc,vl,vr)+query(rc,vl,vr);
}
inline node chain_q(int x,int y)
{
node ans1=(node){0,0,0,0,0,0},ans2=(node){0,0,0,0,0,0};
int fx=top[x],fy=top[y];
while(fx != fy)
{
if(dep[fx] < dep[fy]) {
ans2=query(1,in[fy],in[y])+ans2;
y=fa[fy];
fy=top[y];
}
else {
ans1=query(1,in[fx],in[x])+ans1;
x=fa[fx];
fx=top[x];
}
}
if(in[y] < in[x]) ans1=query(1,in[y],in[x])+ans1;
else ans2=query(1,in[x],in[y])+ans2;
swap(ans1.pre,ans1.suf);
return ans1+ans2;
}
inline void chain_u(int x,int y,int c)
{
int fx=top[x],fy=top[y];
while(fx != fy)
{
if(dep[fx] < dep[fy]) swap(x,y),swap(fx,fy);
upd(1,in[fx],in[x],c);
x=fa[fx];
fx=top[x];
}
if(in[x] > in[y]) swap(x,y);
upd(1,in[x],in[y],c);
}
void init()
{
cnt=0,dfsc=0,dep[1]=0;
for(int i=1;i<=n;i++)
{
scanf("%d",a+i);
head[i]=0;
son[i]=0;
}
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d %d",&u,&v);
add(u,v),add(v,u);
}
}
int main()
{
scanf("%d",&n);
init();
dfs1(1,1);
dfs2(1,1);
build(1,1,n);
int m;
scanf("%d",&m);
while(m--)
{
int f,x,y,c;
scanf("%d",&f);
if(f==1)
{
scanf("%d %d",&x,&y);
printf("%d\n",max(0,chain_q(x,y).ans));
}
else
{
scanf("%d %d %d",&x,&y,&c);
chain_u(x,y,c);
}
}
return 0;
}
应该是最难的一题了咕咕咕
|