775.山海經
hill.in
輸出文件:hill.out
簡單對比
【問題描述】
“南山之首日鵲山。其首日招搖之山,臨於西海之上,多桂,多金玉。有草焉,其狀如韭而青華,其名日祝餘,食之不飢……又東三百里,日堂庭之山,多棪木,多白猿,多水玉,多黃金。
又東三百八十里,日猨翼之山,其中多怪獸,水多怪魚,多白玉,多蝮蟲,多怪蛇,名怪木,不可以上。……”
《山海經》是以山爲綱,以海爲線記載古代的河流、植物、動物及礦產等情況,而且每一條記錄路線都不會有重複的山出現。某天,你的地理老師想重遊《山海經》中的路線,爲了簡化問題,老師已經把每座山用一個整數表示他對該山的喜惡程度,他想知道第a座山到第b座山的中間某段路 。能使他感到最滿意,即 這條路上所有山的喜惡度之和是 最大值。於是老師便向你請教,你能幫助他嗎?值得注意的是,在《山海經》中,第 座山只能到達第 座山。
【輸入】
輸入第1行是兩個數, , , , , 表示一共有 座山, 表示老師想查詢的數目。
第2行是 個整數,代表 座山的喜惡度,絕對值均小於 。
以下 行每行有 , 兩個數, ,表示第 座山到第 座山。
【輸出】
一共有 行,每行有3個數 , , ,表示從第 座山到第 座山總的喜惡度爲 。顯然,對於每個查詢,有 ,如果有多組解,則輸出 最小的,如果 也相等,則輸出 最小的解。
【輸入樣例】
5 3
5 -6 3 -1 4
1 3
1 5
5 5
【輸出樣例】
1 1 5
3 5 6
5 5 4
【題解】
題目大意:給定 個數,求 區間的最大連續子序列的左端點,右端點以及它的和。
首先考慮DP,DP可以快速求 區間的最大連續子序列,但對於 區間的情況就束手無策了。
想到區間問題有很多用線段樹求,那麼我們就可以用線段樹來做這道題。
令 爲 區間的最大連續子序列的和,那麼 。
碼一遍代碼,發現連樣例都過不去,仔細想,如果 且 ,那麼這樣沒有算到。
那我們再定義 ,分別爲從 往前推的最大連續子序列的和,從 往後推的最大連續子序列的和,因爲線段樹的性質,可得這段子序列的左端點 ,右端點 ,那麼
這樣就把所有情況考慮到了。
對於維護 ,
易得
其中 是 到 的子序列和。
同理。
又因爲需要知道左右端點,再開4個量維護 這段子序列的左端點, 這段子序列的右端點,以及 的左右端點即可。
那麼這道題就完美解決了。
#include<cstdio>
#include<algorithm>
using namespace std;
struct ad{
//L,R表示這個節點覆蓋的線段的左端點和右端點,但因爲是線段樹,可以快速得出L和R的值,在此就不定義了
int LSum,RL; //LSum表示從L往後,最大的連續喜惡值之和,RL表示LSum這段連續子序列的右端點
int RSum,LR; //RSum表示從R往前,最大的連續喜惡值之和,LR表示RSum這段連續子序列的左端點
int Sum; //Sum爲L到R的數字之和
int Max,LMax,RMax; //Max表示L到R中最大的連續喜惡值之和,LMax表示這段連續子序列的左端點,RMax表示這段連續子序列的右端點
inline void newnode(int data,int id){LSum=RSum=Sum=Max=data;RL=LR=LMax=RMax=id;}
//給節點賦初值,因爲只有l==R時需要賦初值,所以LSum,RSum,Sum,Max都等於A[L],RL,LR,LMax,RMax都等於L
}T[400005];
int N,M,A[100005];
inline int read(){
int Res=0,f=1;char ch=getchar();
while (ch>'9'||ch<'0') f=(ch=='-')?-f:f,ch=getchar();
while (ch>='0'&&ch<='9') Res=Res*10+ch-'0',ch=getchar();
return Res*f;
}
inline void updata(ad &P,ad A,ad B){ //更新
P.LSum=A.LSum;P.RL=A.RL;
P.RSum=B.RSum;P.LR=B.LR;
P.Sum=A.Sum+B.Sum;
P.Max=A.Max;P.LMax=A.LMax;P.RMax=A.RMax;
if (A.Sum+B.LSum>P.LSum) P.LSum=A.Sum+B.LSum,P.RL=B.RL;
if (B.Sum+A.RSum>P.RSum) P.RSum=B.Sum+A.RSum,P.LR=A.LR;
if (A.RSum+B.LSum>P.Max){//題目要求有多組解輸出i最小的,所以先判這一段
P.Max=A.RSum+B.LSum;
P.LMax=A.LR;P.RMax=B.RL;
}
if (B.Max>P.Max){
P.Max=B.Max;
P.LMax=B.LMax;P.RMax=B.RMax;
}
}
inline void build(int p,int L,int R){
if (L==R){T[p].newnode(A[L],L);return ;}
int mid=(R+L)>>1;
if (L<=mid) build(p<<1,L,mid);
if (R>mid) build(p<<1|1,mid+1,R);
updata(T[p],T[p<<1],T[p<<1|1]);
}
inline ad query(int p,int L,int R,int qL,int qR){ //qL,qR表示詢問的L,R
if (qL==L&&qR==R) return T[p];
if (L==R) return T[p];
int mid=(L+R)>>1;
if (mid>=qR) return query(p<<1,L,mid,qL,qR); //如果 [L,mid] 區間包含 [qL,qR] 就直接跑下去
if (qL>mid) return query(p<<1|1,mid+1,R,qL,qR); //如果 [mid+1,R] 區間包含 [qL,qR] 就直接跑下去
ad Ans1=query(p<<1,L,mid,qL,mid),Ans2=query(p<<1|1,mid+1,R,mid+1,qR),Ans; //有交錯區間就和普通線段樹一樣,刷最大的
updata(Ans,Ans1,Ans2);
return Ans;
}
int main()
{
freopen("hill.in","r",stdin);
freopen("hill.out","w",stdout);
N=read();M=read();
for (int i=1;i<=N;i++) A[i]=read();
build(1,1,N);
for (int i=1;i<=M;i++) {
int L=read(),R=read();
ad Res=query(1,1,N,L,R);
printf("%d %d %d\n",Res.LMax,Res.RMax,Res.Max);
}
return 0;
}