AcWing 1273. 天才的記憶(線段樹/ST表)

整理的算法模板:ACM算法模板總結(分類詳細版)

 

從前有個人名叫 WNB,他有着天才般的記憶力,他珍藏了許多許多的寶藏。

在他離世之後留給後人一個難題(專門考驗記憶力的啊!),如果誰能輕鬆回答出這個問題,便可以繼承他的寶藏。

題目是這樣的:給你一大串數字(編號爲 11 到 NN,大小可不一定哦!),在你看過一遍之後,它便消失在你面前,隨後問題就出現了,給你 MM 個詢問,每次詢問就給你兩個數字 A,BA,B,要求你瞬間就說出屬於 AA 到 BB 這段區間內的最大數。

一天,一位美麗的姐姐從天上飛過,看到這個問題,感到很有意思(主要是據說那個寶藏裏面藏着一種美容水,喝了可以讓這美麗的姐姐更加迷人),於是她就竭盡全力想解決這個問題。

但是,她每次都以失敗告終,因爲這數字的個數是在太多了!

於是她請天才的你幫他解決。如果你幫她解決了這個問題,可是會得到很多甜頭的哦!

輸入格式

第一行一個整數 NN 表示數字的個數。

接下來一行爲 NN 個數,表示數字序列。

第三行讀入一個 MM,表示你看完那串數後需要被提問的次數。

接下來 MM 行,每行都有兩個整數 A,BA,B。

輸出格式

輸出共 MM 行,每行輸出一個數,表示對一個問題的回答。

數據範圍

1≤N≤2×1051≤N≤2×105,
1≤M≤1041≤M≤104

輸入樣例:

6
34 1 8 123 3 2
4
1 2
1 5
3 4
2 3

輸出樣例:

34
123
123
8

 區間查詢最大值,並且是離線靜態的,很明顯用ST表,存儲時間複雜度 O(nlogn),查詢速度O(1);

如果用線段樹,存儲和查詢的時間複雜度都是 O(nlogn);但是這道題數據比較水,都能過;

ST表:

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+7,M=20;
int dp[N][M],a[N];
int main()
{
    int n;
    cin >>n;
    for(int i=1;i<=n;i++) cin >>a[i];
    for(int j=0;j<M;j++)
    {
        for(int i=1;i+(1<<j)-1<=n;i++)
        {
            if(!j) dp[i][j]=a[i];
            else dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
        }
    }
    int q;
    cin >>q;
    while(q--)
    {
        int a,b;
        cin >>a>>b;
        int res=log(b-a+1)/log(2);
        cout <<max(dp[a][res],dp[b-(1<<res)+1][res])<<endl;
    }
    
}

線段樹:

#include <bits/stdc++.h>
using namespace std;
const int N=2*100005;

int w[N];//區間裏的數 
int n,m;
 
struct node
{
	int l,r;   //當前結點所處區間 
	int maxx;
}tr[N*4];//注意對題中所給操作數量或者數據要開4倍大 
 
 
void pushup(int u)// 由子節點更新父節點 
{
	tr[u].maxx=max(tr[u<<1].maxx,tr[u<<1|1].maxx);
}
 
void build(int u,int l,int r)
{
	if(l==r) tr[u]={l,r,w[r]};//如果處理到葉結點了,就保存葉結點的信息 
	else
	{	tr[u]={l,r};	//保存當前節點的區間信息 
		int mid=l+r>>1;	
		build(u<<1,l,mid);	//遞歸左節點 
		build(u<<1|1,mid+1,r);	//	遞歸右節點 
		pushup(u);	//每次根據子節點更新父節點 
	}
}
 
node query(int u,int l,int r)   //在區間l,r裏面查詢 
{
	if(tr[u].l>=l&&tr[u].r<=r) return tr[u]; // 如果當前區間在l~r裏面,則直接返回想要的信息 
	else
	{	
		int mid=tr[u].l+tr[u].r>>1;  //取當前節點的區間中點 
		if(r<=mid) return query(u<<1,l,r);	//	如果當前查詢區間在當前區間的中點左端,則遞歸左兒子 
		else if(l>mid) return query(u<<1|1,l,r);	//如果當前查詢區間在當前節點區間的右端,則遞歸右兒子; 
		else //如果一部分在mid左邊,一部分在mid右邊 
		{
			auto left=query(u<<1,l,r);   //遞歸左兒子 
			auto right=query(u<<1|1,l,r);	//遞歸右兒子 
			node res;
			res.maxx=max(left.maxx,right.maxx) ; //由左兒子和右兒子的信息來更新當前父節點的信息 
			return res;
		}	
	}
}
int main()
{
    int n;
    cin >>n;
    for(int i=1;i<=n;i++) cin >>w[i];
    build(1,1,n);
    int m;
    cin >>m;
    for(int i=0;i<m;i++)
    {
        int l,r;
        cin >>l>>r;
        cout <<query(1,l,r).maxx<<endl;
    }
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章