POJ3264 Balanced Lineup
2605 ワード
http://poj.org/problem?id=3264
クラシックRMQタイトル.RMQ問題は,与えられた区間における最値を求める問題である.素朴アルゴリズムはO(n)であり,線分ツリーを用いてアルゴリズムをO(logn)(線分ツリーに線分の最値を保存する)に最適化できる.
ただし、クエリーのみではRMQアルゴリズムが最適です.O(nlogn)の前処理後にO(1)のクエリー効率を実現できます.線分ツリーの主な違いは区間を修正できることで、RMQはできません.以下、Sparse Tableアルゴリズムを前処理とクエリーの2つの部分に分けて説明します.倍増思想を用いました.前処理:最大値を求める例として、d[i,j]を[i,i+2^j-1]として表しますこの区間内の最大値は、[a,b]区間の最大値を尋ねると、答えはmax(d[a,k],d[b-2^k+1,k])であり、kは2^k<=b-a+1(すなわち長さ)を満たす最大のk、すなわちk=[ln(b-a+1)/ln(2)]である.dの求め方は、動的に計画することができ、d[i,j]=max(d[i,j-1],d[i+2^(j-1),j-1])である.したがって、条件に合致するすべてのdを下から上へのアルゴリズムで繰り返すことができる(i,j)の値.
クエリー:クエリーの巧みな点は[m,n]を2つの部分が重なる区間に分けることです.そこで、[m,n]を2^kの長さの区間に分けることができます.[m,m+2^k-1],[n-2^k+1,n];
この問題のいくつかの注意点:1.繰返しに注意するときは長さ2^0から2^jで繰返し、jのループは外に置く;2.log関数を使うときは、中のパラメータはdouble;3.2を求めるpowerはよく使われる操作で、左シフト<<操作が非常に適切である;4.cin/coutはタイムアウトし、scanf/printfを使う.
クラシックRMQタイトル.RMQ問題は,与えられた区間における最値を求める問題である.素朴アルゴリズムはO(n)であり,線分ツリーを用いてアルゴリズムをO(logn)(線分ツリーに線分の最値を保存する)に最適化できる.
ただし、クエリーのみではRMQアルゴリズムが最適です.O(nlogn)の前処理後にO(1)のクエリー効率を実現できます.線分ツリーの主な違いは区間を修正できることで、RMQはできません.以下、Sparse Tableアルゴリズムを前処理とクエリーの2つの部分に分けて説明します.倍増思想を用いました.前処理:最大値を求める例として、d[i,j]を[i,i+2^j-1]として表しますこの区間内の最大値は、[a,b]区間の最大値を尋ねると、答えはmax(d[a,k],d[b-2^k+1,k])であり、kは2^k<=b-a+1(すなわち長さ)を満たす最大のk、すなわちk=[ln(b-a+1)/ln(2)]である.dの求め方は、動的に計画することができ、d[i,j]=max(d[i,j-1],d[i+2^(j-1),j-1])である.したがって、条件に合致するすべてのdを下から上へのアルゴリズムで繰り返すことができる(i,j)の値.
クエリー:クエリーの巧みな点は[m,n]を2つの部分が重なる区間に分けることです.そこで、[m,n]を2^kの長さの区間に分けることができます.[m,m+2^k-1],[n-2^k+1,n];
この問題のいくつかの注意点:1.繰返しに注意するときは長さ2^0から2^jで繰返し、jのループは外に置く;2.log関数を使うときは、中のパラメータはdouble;3.2を求めるpowerはよく使われる操作で、左シフト<<操作が非常に適切である;4.cin/coutはタイムアウトし、scanf/printfを使う.
#include <iostream>
#include <cmath>
using namespace std;
#define LEN 50005
#define LOG_LEN 20
#define MAX(a,b) (a>b?a:b)
#define MIN(a,b) (a<b?a:b)
#define ABS(a) (a<0?-a:a)
const int INF (1<<30);
int cow[LEN];
int fmin[LEN][LOG_LEN];
int fmax[LEN][LOG_LEN];
int Q;
int N;
void init()
{
memset(cow, 0, sizeof(cow));
memset(fmin, 0, sizeof(fmin));
memset(fmax, 0, sizeof(fmax));
scanf("%d%d", &N, &Q);
for (int i = 1; i <= N; i++)
{
scanf("%d", &cow[i]);
}
}
void build()
{
// build the RMQ
for (int i = 1; i <= N; i++)
{
fmin[i][0] = cow[i];
fmax[i][0] = cow[i];
}
int size = int (log((double)(N + 1)) / log(2.0));
for (int j = 1; j <= size; j++)
{
for (int i = 1; i + (1 << j) - 1 <= N; i++)
{
fmin[i][j] = MIN(fmin[i][j - 1], fmin[i + (1 << (j - 1))][j-1]);
fmax[i][j] = MAX(fmax[i][j - 1], fmax[i + (1 << (j - 1))][j-1]);
}
}
}
int query(int left, int right)
{
int diff = right - left + 1;
int k = (int) (log((double)diff) / log(2.0));
int min = MIN(fmin[left][k], fmin[right - (1 << k) + 1][k]);
int max = MAX(fmax[left][k], fmax[right - (1 << k) + 1][k]);
return max - min;
}
int main()
{
init();
build();
// query
for (int i = 0; i < Q; i++)
{
int x, y;
scanf("%d%d", &x, &y);
printf("%d
", query(x, y));
}
return 0;
}