1. 程式人生 > 其它 >題解 洛谷 P3793 由乃救爺爺

題解 洛谷 P3793 由乃救爺爺

1. 題面描述

題目連結

給定長為 \(n\) 的序列,\(m\) 次詢問,查詢區間最大值。

\(n,m\le 10^7\),資料隨機。

2. 分析

經典的靜態區間最小值問題,經典題目配經典演算法,考慮到 ST 表和笛卡爾樹。

而時間複雜度為 \(O(nlogn)\) 的 ST 表無法通過本題,於是考慮笛卡爾樹。

首先構建一顆笛卡爾樹。

根據笛卡爾樹性質可知,對於區間 \([l,r]\),最大值即為笛卡爾樹上 \(lca(l,r)\) 的值。

說明:

根據二叉搜尋樹性質,可知 \(l\le lca(l,r)\le r\),滿足最大值在 \([l,r]\) 範圍內。

根據大根堆性質,可知 \(lca(l,r)\)

為區間 \([l,r]\) 的最大值。

於是問題就轉變為 \(n\) 個節點的樹,\(m\) 次詢問,查詢 LCA。

這裡可以選擇 Tarjan 演算法離線以 \(O(m)\) 的時間複雜度求解 LCA。

不過因為本體資料隨機,笛卡爾樹期望樹高為 \(logn\) 數量級。同時詢問隨機,每次詢問所查詢的 LCA 不會過深,於是可以考慮從根開始暴力尋找 LCA。經測試,可以通過本題。

3. 程式碼

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

namespace GenHelper
{
    unsigned z1,z2,z3,z4,b;
    unsigned rand_()
    {
    b=((z1<<6)^z1)>>13;
    z1=((z1&4294967294U)<<18)^b;
    b=((z2<<2)^z2)>>27;
    z2=((z2&4294967288U)<<2)^b;
    b=((z3<<13)^z3)>>21;
    z3=((z3&4294967280U)<<7)^b;
    b=((z4<<3)^z4)>>12;
    z4=((z4&4294967168U)<<13)^b;
    return (z1^z2^z3^z4);
    }
}
void srand(unsigned x)
{using namespace GenHelper;
z1=x; z2=(~x)^0x233333333U; z3=x^0x1234598766U; z4=(~x)+51;}
int read()
{
    using namespace GenHelper;
    int a=rand_()&32767;
    int b=rand_()&32767;
    return a*32768+b;
}

int n,m,s,top;
int a[N],son[N][2],sta[N];

int main() {
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	cin>>n>>m>>s;
	srand(s);
	for (int i=1; i<=n; i++) {
		a[i]=read();
		while (top && a[sta[top]]<a[i]) son[i][0]=sta[top--];
		if (top) son[sta[top]][1]=i;
		sta[++top]=i;
	}
	int rt=sta[1];
	unsigned long long ans=0;
	while (m--) {
		int l=read()%n+1,r=read()%n+1;
		if (l>r) swap(l,r);
		int p=rt;
		while (true) {
			if (l<=p && p<=r) {ans+=a[p]; break;}
			p=son[p][p<l];
		}
	}
	cout<<ans<<'\n';
	return 0;
}

本文完。