Nguồn đề bài: http://vn.spoj.com/problems/GSS/
1. Đề bài GSS SPOJ
Cho dãy số a[1], a[2], …, a[n] (|a[i]| <= 15000, n <= 50000).
Hàm q(x, y) = max { tổng(a[i]+a[i+1]+…+a[j]), x <= i <= j <= y }.
Cho m câu hỏi dạng x, y (1 <= x <= y <= n). (m <= 50000) -> hãy tính các q(x, y).
Input
– Dòng đầu là n.
– Dòng thứ hai là dãy a.
– Dòng thứ 3 là m.
– m dòng tiếp theo mỗi dòng là 1 cặp số x, y.
Output
-> Lần lượt ghi ra các q(x, y) tương ứng. Mỗi kết quả ghi trên 1 dòng.
Example
Input:
3
-1 2 3
1
1 2
Output:
2
2. Thuật toán GSS SPOJ
Dùng cây IT với mỗi nút r ta sẽ lưu 4 giá trị: đoạn con có tổng lớn nhất bên trái(left), bên phải (right), tổng của các phần tử của cây con nút r (sum), tổng đoạn con lớn nhất (ans).
Để khởi tạo được những giá trị này ta có công thức như sau:
it[r].sum=it[2*r].sum+it[2*r+1].sum; it[r].left=max(it[2*r].left,it[2*r].sum+it[2*r+1].left); it[r].right=max(it[2*r+1].right,it[2*r+1].sum+it[2*r].right); it[r].ans=max(max(it[2*r].ans,it[2*r+1].ans),it[2*r].right+it[2*r+1].left);
Sau khi đã tính đc như trên,với mỗi truy vấn q(x,y): nếu nút r đang xét nằm gọn trong khoảng cần tìm thì trả về cả 1 cặp 4 phần tử (left,right,sum,ans). Nếu hoàn toàn nằm ngoài khoảng đang xét thì trả về sum=0, left=right=ans=-oo;
Trường hơp còn lại thì trả về giá trị lớn nhất hơp bởi 2 nút con.kết quả cần in ra chính là it[r].ans;
Mình nói hơi kho hiểu, có gì thắc mắc các bạn để lại cmt phía dưới, mình sẽ giải đáp cho nhé.
3. Code tham khảo GSS SPOJ
//saker1417
#include <bits/stdc++.h>
#define left first.first
#define right first.second
#define sum second.first
#define ans second.second
#define maxn 50001
#define oo 1000000001
using namespace std;
long long n,a[maxn],m;
typedef pair < long long , long long > II;
typedef pair < II , II > IIII;
IIII it[4*maxn];
void doc()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
}
void khoitao(int r,int k,int l)
{
if(k==l)
{
it[r]=IIII(II(a[k],a[k]),II(a[k],a[k]));
return;
}
int g=(k+l)/2;
khoitao(2*r,k,g);
khoitao(2*r+1,g+1,l);
it[r].sum=it[2*r].sum+it[2*r+1].sum;
it[r].left=max(it[2*r].left,it[2*r].sum+it[2*r+1].left);
it[r].right=max(it[2*r+1].right,it[2*r+1].sum+it[2*r].right);
it[r].ans=max(max(it[2*r].ans,it[2*r+1].ans),it[2*r].right+it[2*r+1].left);
}
IIII get(int r,int k,int l,int u,int v)
{
if(v<k||l<u) return IIII(II(0,-oo),II(-oo,-oo));
if(u<=k&&l<=v) return it[r];
int g=(k+l)/2;
IIII t1=get(2*r,k,g,u,v);
IIII t2=get(2*r+1,g+1,l,u,v);
long long left1=max(t1.left,t1.sum+t2.left);
long long right1=max(t2.right,t2.sum+t1.right);
long long ans1=max(max(t1.ans,t2.ans),t1.right+t2.left);
long long sum1=t1.sum+t2.sum;
return IIII(II(left1,right1),II(sum1,ans1));
}
void tinh()
{
khoitao(1,1,n);
int m;
cin>>m;
while(m--)
{
int u,v;
cin>>u>>v;
cout<<get(1,1,n,u,v).ans<<endl;
}
}
int main()
{
freopen("GSS.inp","r",stdin);
freopen("GSS.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
doc();
tinh();
}
nếu hoàn toàn nằm ngoài khoảng đang xét thì trả về sum=0,left=right=ans=-oo
thì phải return IIII(II(-oo,-oo),II(0,-oo)); mới đúng chứ nhỉ?
ý tưởng sao lại đưa đến lưu các thông tin ở mỗi nút là như trên như thế nào vậy ad