题目大意:给你一个N个数字的序列和M个问题,问题的内容是给出一个区间[x,y],然后求出这个区间的满足x <= a <= b <= y,且Da+ Da+1 + ...+ Db的和尽量大,如果有多组满足条件的,a,b就尽量小
解题思路:这题的线段树比较复杂,以前的线段树只维护一个值而已,现在的线段树维护的是三个值,前缀和pre,连续和sub,后缀和suf,要求区间[x,y]的满足条件的a,b无非有三种情况,假设现在的区间时的[l,r],他的两个子区间分别为,左子区间[l,mid],右子区间[mid+1,r]
1.所给区间[x,y]在左子区间,满足条件的a,b就等于max_sub(l,mid)
2.所给区间[x,y]在右子区间,满足条件的a,b就等于max_sub(mid+1,r)
#include
#include
#include
using namespace std;
const int maxn = 500010 << 2;
long long sum[maxn], sub[maxn], pre[maxn], suf[maxn];
int sub_s[maxn], sub_e[maxn];
int pre_s[maxn], pre_e[maxn];
int suf_s[maxn], suf_e[maxn];
struct tree_node{
long long pre_v, sub_v, suf_v, sum;
int pr_s, pr_e, sb_s, sb_e, sf_s, sf_e;
};
void push_up(int u) {
int l = u * 2;
int r = u * 2 + 1;
sum[u] = sum[l] + sum[r];
long long MAX = pre[l];
//pre 如果左边的和加上右边的前缀大于以前的前缀
int start = pre_s[l], end = pre_e[l];
if(pre[l] < sum[l] + pre[r]) {
MAX = sum[l] + pre[r];
start = pre_s[l];
end = pre_e[r];
}
pre[u] = MAX; pre_s[u] = start; pre_e[u] = end;
//后缀,如果右边的和加上左边的后缀大于等于以前的后缀
MAX = suf[r]; start = suf_s[r] ; end = suf_e[r];
if( suf[r] <= sum[r] + suf[l]) {
MAX = sum[r] + suf[l];
start = suf_s[l];
end = suf_e[r];
}
suf[u] = MAX; suf_s[u] = start; suf_e[u] = end;
//连续和,不是左边或右边的连续和,就是中间的
MAX = sub[l]; start = sub_s[l]; end = sub_e[l];
if(MAX < suf[l] + pre[r]) {
MAX = suf[l] + pre[r];
start = suf_s[l];
end = pre_e[r];
}
if(MAX < sub[r]) {
MAX = sub[r];
start = sub_s[r];
end = sub_e[r];
}
sub[u] = MAX; sub_s[u] = start; sub_e[u] = end;
}
void build(int u, int l , int r) {
if(l == r) {
scanf("%lld",&sum[u]);
sub[u] = pre[u] = suf[u] = sum[u];
sub_s[u] = sub_e[u] = l;
pre_s[u] = pre_e[u] = l;
suf_s[u] = suf_e[u] = l;
return ;
}
int mid = (l + r) / 2;
build(2 * u, l, mid);
build(2 * u + 1, mid + 1, r);
push_up(u);
}
tree_node query(int l, int r,int u, int L, int R) {
if(L <= l && r <= R) {
tree_node tn;
tn.pre_v = pre[u]; tn.sub_v = sub[u]; tn.suf_v = suf[u];
tn.pr_s = pre_s[u]; tn.pr_e = pre_e[u];
tn.sb_s = sub_s[u]; tn.sb_e = sub_e[u];
tn.sf_s = suf_s[u]; tn.sf_e = suf_e[u];
tn.sum = sum[u];
return tn;
}
int left = 0, right = 0;
int mid = (l + r) / 2;
tree_node t1, t2;
if(L <= mid) {
t1 = query(l,mid,2*u,L,R);
left = 1;
}
if(R > mid) {
t2 = query(mid+1,r,2*u+1,L,R);
right = 1;
}
tree_node tn;
if(left && !right)
tn = t1;
if(!left && right)
tn = t2;
if(left && right) {
//前缀和
tn.pre_v = t1.pre_v; tn.pr_s = t1.pr_s; tn.pr_e = t1.pr_e;
if(tn.pre_v < t1.sum + t2.pre_v) {
tn.pre_v = t1.sum + t2.pre_v;
tn.pr_s = t1.pr_s;
tn.pr_e = t2.pr_e;
}
tn.suf_v = t2.suf_v; tn.sf_s = t2.sf_s; tn.sf_e = t2.sf_e;
if(tn.suf_v <= t2.sum + t1.suf_v) {
tn.suf_v = t2.sum + t1.suf_v;
tn.sf_s = t1.sf_s;
tn.sf_e = t2.sf_e;
}
tn.sub_v = t1.sub_v; tn.sb_s = t1.sb_s; tn.sb_e = t1.sb_e;
if(tn.sub_v < t1.suf_v + t2.pre_v) {
tn.sub_v = t1.suf_v + t2.pre_v;
tn.sb_s = t1.sf_s;
tn.sb_e = t2.pr_e;
}
if(tn.sub_v < t2.sub_v) {
tn.sub_v = t2.sub_v;
tn.sb_s = t2.sb_s;
tn.sb_e = t2.sb_e;
}
tn.sum = t1.sum + t2.sum;
}
return tn;
}
int main() {
int N, M, mark = 1;
while(scanf("%d%d",&N, &M) == 2) {
build(1,1,N);
printf("Case %d:\n",mark++);
int t1, t2;
for(int i = 0; i < M; i++) {
scanf("%d%d",&t1,&t2);
tree_node t3 = query(1,N,1,t1,t2);
printf("%d %d\n",t3.sb_s, t3.sb_e);
}
}
return 0;
}
3.x在区间[l,mid]中,y在区间[mid+1,r]中,那么a,b就等于(max_suf(l,mid),max_pre(mid+1,r)),左子区间的后缀和右子区间的前缀