[线段树] (j) uva1400* (区间查询和定位尝试)(难题)
uva1400 "Ray, Pass me the dishes!"
如在阅读本文时遇到不懂的部分,请在评论区询问,或跳转 线段树总介绍
这题在luogu上是黑题呢...QAQ(但是却是一道线段树模版题啊qwq)
题目大意:给出一个长度为n的整数序列D,对m个询问做出回答,对询问(a,b)找到(x,y)使得a<=x<=y<=b且Dx+Dx+1+……+Dy最大。如有多组答案取字典序最小的一组(即x,y最小)。
下面是无关紧要的一点废话(一点一点的解释代码)
因为要输出两个数(x,y)显然不能用普通的返回值了,那么这里选择用一个结构体类型data来作返回值,其l,r就是要求的x,y
为了能够比较大小(字典序)又写了 彩虹般的 重载运算符,友元函数。(也可以使用pair)
又用了前缀和sum[]来进行优化。
要注意的是如果用重载运算符,可以选择定义一个val或者用前缀和,
但是如果用pair,则一定要写重载运算符(因为不能有三个量)。
当然你也可以选择手动写个比较函数
struct data{ int l,r; long long val(){return sum[r]-sum[l-1];} friend bool operator > (data a,data b){ return a.val()>b.val()||(a.val()==b.val()&&(a.l<b.l||(a.l==b.l&&a.r<b.r))); } friend bool operator < (data a,data b){ return a.val()<b.val()||(a.val()==b.val()&&(a.l>b.l||(a.l==b.l&&a.r>b.r))); } };
defmax是一个data型的max....
data defmax(data a,data b){ return a>b?a:b; }
大概写成这样会更好?(那就什么类型都能用啦...woc难道还要比较其他类型吗?)
auto defmax(auto a,auto b){ return a>b?a:b; }
pushup:
Maxn比较左、右、中间
pre比较左、左+右
suf同理
void pushup(int rt){ Maxn[rt]=defmax(defmax(Maxn[LS],Maxn[RS]),(data){suf[LS].l,pre[RS].r}); //left right and middle pre[rt]=defmax(pre[LS],(data){pre[LS].l,pre[RS].r}); suf[rt]=defmax(suf[RS],(data){suf[LS].l,suf[RS].r}); //注意此处要看大小而不是区间长度 }
build,就是build
void build(int rt,int l,int r){ if(l==r){ pre[rt]=suf[rt]=Maxn[rt]=(data){l,r}; return; }int mid=l+r>>1; build(LS,l,mid); build(RS,mid+1,r); pushup(rt); return; }
query
如果包含 直接返回该节点的最大区间
不在区间内判断左右(整区间)
在中间 在左边找后继右边找前驱并拼凑,与纯左、纯右对比取大
data query(int rt,int l,int r,int x,int y){ if(x<=l&&y>=r)return Maxn[rt]; int mid=l+r>>1; if(y<=mid) //左儿子 return query(LS,l,mid,x,y); if(x>mid) //右儿子 return query(RS,mid+1,r,x,y); //在中间 data a=defmax(query(LS,l,mid,x,y),query(RS,mid+1,r,x,y)); data b=(data){asksuf(LS,l,mid,x,y).l,askpre(RS,mid+1,r,x,y).r}; return defmax(a,b); }
找前驱/后继(对称版)
后继一直往右找,前驱往左找,保证最后正好拼凑起来
data asksuf(int rt,int l,int r,int x,int y){ //在左子树内找后继(r == y) if(suf[rt].l>=x)return suf[rt]; //此区间后继一定 > 儿子后继 int mid=l+r>>1; if(x>mid)return asksuf(RS,mid+1,r,x,y); return defmax((data){asksuf(LS,l,mid,x,y).l,r},suf[RS]); } data askpre(int rt,int l,int r,int x,int y){ if(pre[rt].r<=y)return pre[rt]; int mid=l+r>>1; if(y<=mid)return askpre(LS,l,mid,x,y); return defmax((data){l,askpre(RS,mid+1,r,x,y).r},pre[LS]); }
代码
QAQ friend竟然没有高亮,自己加了个生态环保绿
/*uva1400*/
#include<iostream> #include<cmath> #include<algorithm> #include<cstdio> using namespace std; const int N=2e6+3; long long sum[N],a[N]; struct data{ int l,r; long long val(){return sum[r]-sum[l-1];} friend bool operator > (data a,data b){ return a.val()>b.val()||(a.val()==b.val()&&(a.l<b.l||(a.l==b.l&&a.r<b.r))); } friend bool operator < (data a,data b){ return a.val()<b.val()||(a.val()==b.val()&&(a.l>b.l||(a.l==b.l&&a.r>b.r))); } }; data defmax(data a,data b){ return a>b?a:b; } namespace Segment_Tree{ #define LS (rt<<1) #define RS (LS|1) data pre[N<<2],suf[N<<2],Maxn[N<<2]; //前驱 后继 最大子段和 void pushup(int rt){ Maxn[rt]=defmax(defmax(Maxn[LS],Maxn[RS]),(data){suf[LS].l,pre[RS].r}); //left right and middle pre[rt]=defmax(pre[LS],(data){pre[LS].l,pre[RS].r}); suf[rt]=defmax(suf[RS],(data){suf[LS].l,suf[RS].r}); //注意此处要看大小而不是区间长度 } void build(int rt,int l,int r){ if(l==r){ pre[rt]=suf[rt]=Maxn[rt]=(data){l,r}; return; }int mid=l+r>>1; build(LS,l,mid); build(RS,mid+1,r); pushup(rt); return; } data asksuf(int rt,int l,int r,int x,int y){ //在左子树内找后继(r == y) if(suf[rt].l>=x)return suf[rt]; //此区间后继一定 > 儿子后继 int mid=l+r>>1; if(x>mid)return asksuf(RS,mid+1,r,x,y); return defmax((data){asksuf(LS,l,mid,x,y).l,r},suf[RS]); } data askpre(int rt,int l,int r,int x,int y){ if(pre[rt].r<=y)return pre[rt]; int mid=l+r>>1; if(y<=mid)return askpre(LS,l,mid,x,y); return defmax((data){l,askpre(RS,mid+1,r,x,y).r},pre[LS]); } data query(int rt,int l,int r,int x,int y){ if(x<=l&&y>=r)return Maxn[rt]; int mid=l+r>>1; if(y<=mid) //左儿子 return query(LS,l,mid,x,y); if(x>mid) //右儿子 return query(RS,mid+1,r,x,y); //在中间 data a=defmax(query(LS,l,mid,x,y),query(RS,mid+1,r,x,y)); data b=(data){asksuf(LS,l,mid,x,y).l,askpre(RS,mid+1,r,x,y).r}; return defmax(a,b); } } using namespace Segment_Tree; int main(){ int n,m,x,y,Case=0; data ans; while(scanf("%d%d", &n, &m)!=EOF){ printf("Case %d:\n",++Case); for(int i=1;i<=n;++i){ scanf("%lld",&a[i]); sum[i]=a[i]+sum[i-1]; //sum会覆盖以前的,可以不初始化 } build(1,1,n); while(m--){ scanf("%d%d",&x,&y); ans=query(1,1,n,x,y); printf("%d %d\n",ans.l,ans.r); } } return 0; }