題解 CF515E 【Drazil and Park】
阿新 • • 發佈:2020-11-24
思路:
\(\quad\)對於此題考慮使用線段樹維護,因為有環,所以斷環為鏈,陣列開兩倍,另外距離換成座標(距離的字首和),假設最後求的兩棵樹分別為 \(x\) , \(y\) \((dis_x<dis_y)\),那麼答案就是 \(2\times (h_x+h_y)+dis_y-dis_x\) ,就是 \((2\times h_x-dis_x)+(2\times h_y+dis_y)\) ,那麼我們線段樹維護這兩個值及答案,一個作答案的左端點( \(x\) ),一個作答案的右端點( \(y\) ),注意要保證 \(dis_x<dis_y\) ,建樹和詢問的程式碼應重點看看,另外詢問返回的值是結構體,數值初始化應為 \(-inf\)
struct node{ int suml,sumr,Max; }t[N<<2]; il void build(int k,int l,int r) { if(l==r){t[k].suml=h[l]+dis[l];t[k].sumr=h[l]-dis[l];t[k].Max=-inf;return;}//suml表示作右端點,sumr表示作左端點 int mid=l+r>>1; build(k<<1,l,mid);build(k<<1|1,mid+1,r); t[k].suml=max(t[k<<1].suml,t[k<<1|1].suml); t[k].sumr=max(t[k<<1].sumr,t[k<<1|1].sumr); t[k].Max=max(t[k<<1].Max,max(t[k<<1|1].Max,t[k<<1|1].suml+t[k<<1].sumr));//取 (左兒子的最大值,右兒子的最大值,左兒子中最適合作左端點的加上右兒子中最適合作右端點的和) 這三者的最大值 } il node query(int k,int l,int r,int x,int y) { if(x<=l&&y>=r)return t[k]; int mid=l+r>>1;node t1,t2,t3; t1.suml=t1.sumr=t1.Max=t2.suml=t2.sumr=t2.Max=t3.suml=t3.sumr=t3.Max=-inf; if(x<=mid)t1=query(k<<1,l,mid,x,y); if(y>mid)t2=query(k<<1|1,mid+1,r,x,y); t3.suml=max(t1.suml,t2.suml); t3.sumr=max(t1.sumr,t2.sumr); t3.Max=max(t1.Max,max(t2.Max,t2.suml+t1.sumr)); return t3; }
\(\quad\)關於變數名的解釋:用陣列 \(h_i\) 表示第 \(i\) 棵樹的高度(或 \(i-n\) ),陣列 \(d_i\) 表示第 \(i\) 棵樹和第 \(i+1\) 棵樹的距離,陣列 \(dis_i\) 表示第 \(i\) 棵樹的座標, \(sumr\) 指最大的可做左端點的數, \(suml\) 指最大的可做右端點的數, \(Max\) 指這個區間的最大答案
\(\quad\)再看看主函式吧,求補集的操作要額外注意
signed main() { n=read();m=read(); for(re i=1;i<=n;i++)d[i]=d[i+n]=read(); for(re i=1;i<=n;i++)h[i]=h[i+n]=read()<<1; for(re i=1;i<=n<<1;i++)dis[i]=dis[i-1]+d[i-1];//注意d[i-1]表示的才是第i-1棵樹與第i棵樹的距離 build(1,1,n<<1);//建樹 while(m--) { re x=read(),y=read(); if(y<x){swap(x,y);x++;y--;} //這個求補集的操作也要額外注意 else {swap(x,y);x++;y=y+n-1;}//這個求補集的操作也要額外注意 print(query(1,1,n<<1,x,y).Max);putchar('\n'); } return 0; }