原文链接http://www.cnblogs.com/zhouzhendong/p/8990745.html
题目传送门 - CodeForces 516C
题意
在一个环上,有$n$棵树。
给出每一个树的高度$h_i$以及每一个树距离他顺时针方向后一个树的距离$d_i$。
有$m$次询问,每次,都会有一段连续区间内的树萎掉。请你找两棵树$x,y$,最大化$2(h_x+h_y)+dist(x,y)$。其中$dist(x,y)$为这两棵树的距离,这个距离不能包含萎掉的树。
$n,mleq 10^5$
题解
闭着眼睛都能想到线段树。
我们首先闭着眼睛给$d_i$求个前缀和。
然后开线段树,维护区间$2h_i-d_{i-1}$的最大值以及$2h_i+d_{i-1}$的最大值,以及区间匹配的两个树的最大的$2(h_x+h_y)+dist(x,y)$。
然后倍长原序列,每次询问就是区间求$max$。
UPD(发表这篇博文约30分钟后):
代码重写了哈哈。又短又漂亮。
代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int N=200005; const LL INF=1LL<<56; int n,m; LL d[N],h[N]; struct Segment{ LL val,v0,v1; Segment(){} Segment(LL a,LL b,LL c){ val=a,v0=b,v1=c; } }t[N<<2]; LL read(){ LL x=0; char ch=getchar(); while (!('0'<=ch&&ch<='9')) ch=getchar(); while ('0'<=ch&&ch<='9') x=x*10+ch-48,ch=getchar(); return x; } Segment merge(Segment ls,Segment rs){ Segment rt; rt.val=max(max(ls.val,rs.val),ls.v0+rs.v1); rt.v0=max(ls.v0,rs.v0); rt.v1=max(ls.v1,rs.v1); return rt; } void build(int rt,int L,int R){ if (L==R){ t[rt].v0=h[L]-d[L-1],t[rt].v1=h[L]+d[L-1],t[rt].val=-INF; return; } int mid=(L+R)>>1,ls=rt<<1,rs=ls|1; build(ls,L,mid); build(rs,mid+1,R); t[rt]=merge(t[ls],t[rs]); } Segment query(int rt,int L,int R,int xL,int xR){ if (L>xR||R<xL||xL>xR) return Segment(-INF,-INF,-INF); if (xL<=L&&R<=xR) return t[rt]; int mid=(L+R)>>1,ls=rt<<1,rs=ls|1; return merge(query(ls,L,mid,xL,xR),query(rs,mid+1,R,xL,xR)); } int main(){ scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) d[i]=read()+d[i-1]; for (int i=1;i<=n;i++) h[i]=read()*2; for (int i=1;i<=n;i++) d[i+n]=d[i]+d[n],h[i+n]=h[i]; build(1,1,n*2); for (int i=1,a,b;i<=m;i++){ scanf("%d%d",&a,&b); if (b>=a) a+=n; printf("%I64d ",query(1,1,n*2,b+1,a-1).val); } return 0; }
题解 - 续 (修改前的题解后一半以及代码)
然而我代码写的很丑。
首先,区间求$max$的时候因为懒,为了少写一点代码,强行把一个$log$的算法写成$log^2$的。
第二,我线段树维护的前两个信息不是最大值,而是编号……于是自找麻烦。
第三,我没有倍长……
(有空重写)
代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int N=100005; int n,m; LL d[N],h[N],val[N<<2]; int M[2][N<<2]; LL read(){ LL x=0; char ch=getchar(); while (!('0'<=ch&&ch<='9')) ch=getchar(); while ('0'<=ch&&ch<='9') x=x*10+ch-48,ch=getchar(); return x; } LL c(int t,int i){ if (i==0) return -(1LL<<55); return t==0?(h[i]-d[i-1]):(h[i]+d[i-1]); } int pushup(int ls,int rs,int t){ return c(t,ls)>c(t,rs)?ls:rs; } void build(int rt,int L,int R){ if (L==R){ M[0][rt]=M[1][rt]=L,val[rt]=-(1LL<<55); return; } int mid=(L+R)>>1,ls=rt<<1,rs=ls|1; build(ls,L,mid); build(rs,mid+1,R); M[0][rt]=pushup(M[0][ls],M[0][rs],0); M[1][rt]=pushup(M[1][ls],M[1][rs],1); val[rt]=max(max(val[ls],val[rs]),c(0,M[0][ls])+c(1,M[1][rs])); } int query(int rt,int L,int R,int xL,int xR,int t){ if (L>xR||R<xL||xL>xR) return 0; if (xL<=L&&R<=xR) return M[t][rt]; int mid=(L+R)>>1,ls=rt<<1,rs=ls|1; return pushup(query(ls,L,mid,xL,xR,t),query(rs,mid+1,R,xL,xR,t),t); } LL query(int rt,int L,int R,int xL,int xR){ if (L>xR||R<xL||xL>xR) return 0; if (xL<=L&&R<=xR) return val[rt]; int mid=(L+R)>>1,ls=rt<<1,rs=ls|1; return max(max(query(ls,L,mid,xL,xR),query(rs,mid+1,R,xL,xR)),c(0,query(ls,L,mid,xL,xR,0))+c(1,query(rs,mid+1,R,xL,xR,1))); } int main(){ scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) d[i]=read()+d[i-1]; for (int i=1;i<=n;i++) h[i]=read()*2; build(1,1,n); for (int i=1,a,b;i<=m;i++){ scanf("%d%d",&a,&b); if (b<a){ printf("%I64d ",query(1,1,n,b+1,a-1)); } else { LL ans=max(query(1,1,n,1,a-1),query(1,1,n,b+1,n)); ans=max(ans,c(1,query(1,1,n,1,a-1,1))+d[n]+c(0,query(1,1,n,b+1,n,0))); printf("%I64d ",ans); } } return 0; }