题意:
给你n个点(小于等于2e3个),每个点有个价值val,有个x,y坐标(这三个值都是1e9的)
让你求一个最大的子矩阵和
题解:
线段树的精髓——区间合并
因为x,y太大,所以先考虑离散化,之后是一个n*m的矩形
枚举上,下边界,线段树维护区间最大子段和
1、合并的答案要么左区间的子段和,要么是右区间的子段和,要么是中间的一部分
2、考虑中间这部分怎么求,也就是说,其实求一个区间的最大,前后缀就行了,因为中间这部分就是
左区间的最大后缀+右区间的最大前缀
3、考虑怎么维护区间的前后缀、前缀的话,肯定是左区间前缀 和 左区间的区间和加右区间的前缀 取一个最大值
后缀同理
详细区间线段树维护 看博客
为了简单起便,只需要查询的是整个区间所有,连询问函数也不用写了,直接找根节点的值就行了
#include
using namespace std;
typedef long long ll;
const int maxn = 1e4+7;
int T,n;
struct node
{
ll lsum,rsum,sum,Ans;
void update(ll x)
{
lsum += x;
rsum += x;
sum += x;
Ans += x;
}
} tree[maxn<<2];
void push_up(int x)
{
tree[x].sum = tree[x<<1].sum+tree[x<<1|1].sum;
tree[x].lsum = max(tree[x<<1].lsum,tree[x<<1].sum+tree[x<<1|1].lsum);
tree[x].rsum = max(tree[x<<1|1].rsum,tree[x<<1|1].sum+tree[x<<1].rsum);
tree[x].Ans = max(max(tree[x<<1].Ans,tree[x<<1|1].Ans),tree[x<<1].rsum+tree[x<<1|1].lsum);
}
void build(int L = 1,int R = n,int x = 1)
{
tree[x].lsum = tree[x].rsum = 0;
tree[x].sum = tree[x].Ans = 0;
if(L == R)return;
int mid = L+R>>1;
build(L,mid,x<<1),build(mid+1,R,x<<1|1);
//push_up(x);
}
void update(int k,ll val,int L=1,int R=n,int x = 1)
{
if(L == R)
{
tree[x].update(val);
}
else
{
int mid = L+R>>1;
if(k<=mid)update(k,val,L,mid,x<<1);
else update(k,val,mid+1,R,x<<1|1);
push_up(x);
}
}
vector vecL,vecU;
vector > vec[maxn];
ll x[maxn],y[maxn],val[maxn];
int main()
{
for(scanf("%d",&T); T--;)
{
vecL.clear();
vecU.clear();
for(int i=1; i<=n; i++)vec[i].clear();
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
scanf("%lld%lld%lld",&x[i],&y[i],&val[i]);
vecL.push_back(x[i]);
vecU.push_back(y[i]);
}
sort(vecL.begin(),vecL.end());
vecL.erase(unique(vecL.begin(),vecL.end()),vecL.end());
sort(vecU.begin(),vecU.end());
vecU.erase(unique(vecU.begin(),vecU.end()),vecU.end());
for(int i=1; i<=n; i++)
{
int x_ = lower_bound(vecL.begin(),vecL.end(),x[i])-vecL.begin()+1;//+1表示从1-n
int y_ = lower_bound(vecU.begin(),vecU.end(),y[i])-vecU.begin()+1;
vec[x_].push_back(make_pair(y_,val[i]));
}
ll ans = 0;
for(int i=1; i<=n; i++)
{
build();
for(int j=i; j<=n; j++)
{
for(auto k:vec[j])update(k.first,k.second);
ans = max(ans,tree[1].Ans);//直接找根节点
}
}
printf("%lld\n",ans);
}
return 0;
}
题意:
给定一序列,每次给定区间l,y查询该区间最大连续子段和
题解:
还是线段树维护连续区间最大子段和,这次是区间查询
#include
using namespace std;
typedef long long ll;
const int maxn = 1e4+7;
int T,n,m;
int a[maxn];
struct node
{
ll lsum,rsum,sum,Ans;
void update(ll x)
{
lsum += x;
rsum += x;
sum += x;
Ans += x;
}
} tree[maxn<<2];
void push_up(int x)
{
tree[x].sum = tree[x<<1].sum+tree[x<<1|1].sum;
tree[x].lsum = max(tree[x<<1].lsum,tree[x<<1].sum+tree[x<<1|1].lsum);
tree[x].rsum = max(tree[x<<1|1].rsum,tree[x<<1|1].sum+tree[x<<1].rsum);
tree[x].Ans = max(max(tree[x<<1].Ans,tree[x<<1|1].Ans),tree[x<<1].rsum+tree[x<<1|1].lsum);
}
void build(int L = 1,int R = n,int x = 1)
{
if(L == R)
{
tree[x].lsum = tree[x].rsum = a[L];
tree[x].sum = tree[x].Ans = a[L];
return;
}
int mid = L+R>>1;
build(L,mid,x<<1),build(mid+1,R,x<<1|1);
push_up(x);
}
node query(int l,int r,int L = 1,int R = n,int x = 1)
{
if (l <= L && R <= r)return tree[x];
int mid=(L+R)>>1;
if (r <= mid) return query(l, r, L, mid, x<<1);
if (mid < l) return query(l, r, mid+1 , R, x<<1|1);
node lo = query(l,r,L,mid,x<<1), ro = query(l,r,mid+1,R,x<<1|1),ans;
ans.sum=lo.sum+ro.sum;
ans.lsum=max(lo.lsum,lo.sum+ro.lsum);
ans.rsum=max(ro.rsum,ro.sum+lo.rsum);
ans.Ans=max(max(lo.Ans,ro.Ans),lo.rsum+ro.lsum);
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1; i<=n; i++)scanf("%d",&a[i]);
build();
scanf("%d",&m);
while(m--)
{
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n", query(x, y).Ans);
}
return 0;
}
题意:
给定长度为N的数列A,以及M条指令,每条指令可能是以下两种之一:
1、“1 x y”,查询区间 [x,y] 中的最大连续子段和,即
2、“2 x y”,把 A[x] 改成 y
对于每个查询指令,输出一个整数表示答案。
题解:
还是线段树维护
这次是上面两个结合版本
对于区间查询需要注意
因为并不能直接区间做加法,返回的结构体嘛
对于查询区间全部包含当前区间时,直接返回结构体即可
否则,在右边往右查,在左边往左查
剩下的就是跨越左右子区间了,那么,返回 合并的左右查询的结构体 就行了
模板一
#include
using namespace std;
typedef long long ll;
const int maxn = 5e5+7;
int T,n,m;
ll a[maxn];
struct node
{
ll lsum,rsum,sum,Ans;
void update(ll x)
{
lsum = x;
rsum = x;
sum = x;
Ans = x;
}
} tree[maxn<<2];
void push_up(int x)
{
tree[x].sum = tree[x<<1].sum+tree[x<<1|1].sum;
tree[x].lsum = max(tree[x<<1].lsum,tree[x<<1].sum+tree[x<<1|1].lsum);
tree[x].rsum = max(tree[x<<1|1].rsum,tree[x<<1|1].sum+tree[x<<1].rsum);
tree[x].Ans = max(max(tree[x<<1].Ans,tree[x<<1|1].Ans),tree[x<<1].rsum+tree[x<<1|1].lsum);
}
void build(int L = 1,int R = n,int x = 1)
{
if(L == R)
{
tree[x].lsum = tree[x].rsum = a[L] = tree[x].sum = tree[x].Ans = a[L];
return;
}
int mid = L+R>>1;
build(L,mid,x<<1),build(mid+1,R,x<<1|1);
push_up(x);
}
void update(int k,ll val,int L=1,int R=n,int x = 1)
{
if(L == R)
{
tree[x].update(val);
}
else
{
int mid = L+R>>1;
if(k<=mid)update(k,val,L,mid,x<<1);
else update(k,val,mid+1,R,x<<1|1);
push_up(x);
}
}
node query(int l,int r,int L = 1,int R = n,int x = 1)
{
if (l <= L && R <= r)return tree[x];
int mid=(L+R)>>1;
if (r <= mid) return query(l, r, L, mid, x<<1);
if (mid < l) return query(l, r, mid+1 , R, x<<1|1);
node lo = query(l,r,L,mid,x<<1), ro = query(l,r,mid+1,R,x<<1|1),ans;
ans.sum=lo.sum+ro.sum;
ans.lsum=max(lo.lsum,lo.sum+ro.lsum);
ans.rsum=max(ro.rsum,ro.sum+lo.rsum);
ans.Ans=max(max(lo.Ans,ro.Ans),lo.rsum+ro.lsum);
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)scanf("%lld",&a[i]);
build();
while(m--)
{
int k,x,y;
scanf("%d%d%d",&k,&x,&y);
if(k==1)
{
if(x>y)swap(x,y);
printf("%lld\n", query(x,y).Ans);
}
else update(x,y);
}
return 0;
}
模板二
/*
线段树维护区间最大子段和
https://www.acwing.com/problem/content/description/246/
*/
#include
using namespace std;
typedef long long ll;
const int maxn = 5e5+7;
int T,n,m;
ll a[maxn];
struct node
{
ll lsum,rsum,sum,Ans;
int l,r;
void update(ll x)
{
lsum = x;
rsum = x;
sum = x;
Ans = x;
}
} tree[maxn<<2];
void push_up(int x)
{
tree[x].sum = tree[x<<1].sum+tree[x<<1|1].sum;
tree[x].lsum = max(tree[x<<1].lsum,tree[x<<1].sum+tree[x<<1|1].lsum);
tree[x].rsum = max(tree[x<<1|1].rsum,tree[x<<1|1].sum+tree[x<<1].rsum);
tree[x].Ans = max(max(tree[x<<1].Ans,tree[x<<1|1].Ans),tree[x<<1].rsum+tree[x<<1|1].lsum);
}
void build(int l,int r,int x)
{
tree[x].l=l;
tree[x].r=r;
if(l == r)
{
tree[x].lsum = tree[x].rsum = tree[x].sum = tree[x].Ans = a[l];
return;
}
int mid = l+r>>1;
build(l,mid,x<<1),build(mid+1,r,x<<1|1);
push_up(x);
}
void update(int x,int k,ll val)
{
int l=tree[x].l;
int r=tree[x].r;
if(l == r)
{
tree[x].update(val);
return ;
}
else
{
int mid = l+r>>1;
if(k<=mid)update(x<<1,k,val);
else update(x<<1|1,k,val);
push_up(x);
}
}
node query(int x,int l,int r)
{
int L=tree[x].l;
int R=tree[x].r;
if(l<=L && R<=r)return tree[x];
int mid=(L+R)>>1;
if (r<=mid) return query(x<<1,l,r);
if (l>mid) return query(x<<1|1,l,r);
node lo = query(x<<1,l,r), ro = query(x<<1|1,l,r),ans;
ans.sum=lo.sum+ro.sum;
ans.lsum=max(lo.lsum,lo.sum+ro.lsum);
ans.rsum=max(ro.rsum,ro.sum+lo.rsum);
ans.Ans=max(max(lo.Ans,ro.Ans),lo.rsum+ro.lsum);
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)scanf("%lld",&a[i]);
build(1,n,1);
while(m--)
{
int k,x,y;
scanf("%d%d%d",&k,&x,&y);
if(k==1)
{
if(x>y)swap(x,y);
printf("%lld\n", query(1,x,y).Ans);
}
else update(1,x,y);
}
return 0;
}