uva 1400 "Ray, Pass me the dishes!" (区间合并 最大子段和+输出左右边界)
题目链接:https://vjudge.net/problem/UVA-1400
题意:给一串序列,求最大子段,如果有多个,输出字典序最小的那个的左右端点
思路:
之前写过类似的,这个麻烦点需要输出左右端点,我们直接再开几个数组维护左右边界就好了,因为pushup和查询都需要合并操作,我们可以改下pushup传的参数,让查询的时候也可以用pushup合并区间信息。
字典序最小的只要合并和查询的时候都优先向左就好了。
实现代码;
#include<bits/stdc++.h> using namespace std; #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define mid int m = (l + r) >> 1 #define ll long long const int M = 5e5+10; int n; ll a[M]; struct node{ ll lsum,rsum,sum,num; int lef,rig,lid,rid; }t[M<<2]; void pushup(node &k,node ls,node rs){ k.num = ls.num + rs.num; if(ls.lsum>=ls.num+rs.lsum) k.lsum=ls.lsum,k.lid=ls.lid; else k.lsum=ls.num+rs.lsum,k.lid=rs.lid; if(rs.rsum>rs.num+ls.rsum) k.rsum=rs.rsum,k.rid=rs.rid; else k.rsum=rs.num+ls.rsum,k.rid=ls.rid; if(ls.sum>=max(rs.sum,ls.rsum+rs.lsum)) k.sum=ls.sum,k.lef=ls.lef,k.rig=ls.rig; else{ if(ls.rsum+rs.lsum>=rs.sum) k.sum=ls.rsum+rs.lsum,k.lef=ls.rid,k.rig=rs.lid; else k.sum=rs.sum,k.lef=rs.lef,k.rig=rs.rig; } } void build(int l,int r,int rt){ if(l == r){ t[rt].lid=t[rt].rid=t[rt].lef=t[rt].rig=l; t[rt].sum=t[rt].num=t[rt].lsum=t[rt].rsum=a[l]; return ; } mid; build(lson); build(rson); pushup(t[rt],t[rt<<1],t[rt<<1|1]); } node query(int L,int R,int l,int r,int rt){ if(L <= l&&R >= r){ return t[rt]; } mid; if(R <= m) return query(L,R,lson); else if(L > m) return query(L,R,rson); else{ node ret; pushup(ret,query(L,R,lson),query(L,R,rson)); return ret; } } int main() { int cas = 1,m,l,r; while(scanf("%d%d",&n,&m)!=EOF){ memset(t,0,sizeof(t)); for(int i = 1;i <= n;i ++) scanf("%lld",&a[i]); build(1,n,1); printf("Case %d:\n",cas++); while(m--){ scanf("%d%d",&l,&r); node ans = query(l,r,1,n,1); printf("%d %d\n",ans.lef,ans.rig); } } return 0; }