zoukankan      html  css  js  c++  java
  • 【洛谷P7599】雨林跳跃

    题目

    题目链接:https://www.luogu.com.cn/problem/P7599
    在苏门答腊岛的热带雨林中,有 (N) 棵树排成一排,从左到右依次用 (0)(N-1) 进行编号,其中 (i) 号树的高度为 (H[i]),且所有树的高度互不相同
    Pak Dengklek 正在训练一只猩猩,让她能够从一棵树上跳到另一棵树上。对于一次跳跃,猩猩可以从一棵树,向左或向右跳到比当前这棵树高的第一棵树上。形式化地,如果猩猩当前在 (x) 号树,那么当且仅当满足下列条件之一时,她能够跳到 (y) 号树上:

    • (y) 是满足 (H[z]>H[x]) 的所有 (z) 中比 (x) 小的最大非负整数;或者:
    • (y) 是满足 (H[z]>H[x]) 的所有 (z) 中比 (x) 大的最小非负整数。

    Pak Dengklek 有 (Q) 个跳跃计划,每个计划用四个整数 (A)(B)(C)(D)(A le B<C le D))来描述。对于每个计划,Pak Dengklek 想知道猩猩是否能够从某棵树 (s)(A le s le B))出发,经过若干次跳跃,到达某棵树 (e)(C le e le D))。若该计划可行,Pak Dengklek 还想知道可行方案中猩猩需要的最少跳跃次数。
    你需要实现下列函数:

    void init(int N, int[] H)

    • (N):树的数量。
    • (H):大小为 (N) 的数组,(H[i]) 表示 (i) 号树的高度。
    • 该函数在第一次 minimum_jumps 的调用前,将会被调用恰好一次。

    int minimum_jumps(int A, int B, int C, int D)

    • (A,B):可以用作起点的树的编号范围。
    • (C,D):可以用作终点的树的编号范围。
    • 该函数需要返回可行方案中猩猩需要的最少跳跃次数,或者返回 (-1) 表示该计划不可行。
    • 该函数将被调用恰好 (Q) 次。

    (nleq 2 imes 10^5;Qleq 10^5)

    思路

    首先考虑 (A=B,C=D) 时怎么做。
    不难发现可以从 (A)(C) 的充要条件是 ([A,C)) 中最高的树都没有 (C) 高。
    最优方案一定是先不断往左右两边更高的跳,直到下一次跳的高度超过 (C) 的高度。然后不断往右跳直到 (C) 为止。
    所以我们预处理 (f[i][j],g[i][j],h[i][j]) 分别表示从 (i) 开始跳 (2^j) 次,其中每次往左右更高的跳 / 往右跳 / 往左跳能跳到的位置;再维护一个 ST 表用于求区间最大值,然后就可以单次 (O(log n)) 查询了。

    接下来考虑 (C=D) 时怎么做。
    我们找到 (C) 左边第一个高度大于 (C) 的位置 (k)。如果 $kgeq $ 则无解,否则从 ([max(k+1,A),B]) 开始跳一定最优。因为其他点可以跳到的,这个位置一定也可以通过不会更多的次数跳到。

    最后考虑 (100\%) 的数据,也就是 (Aleq B<Cleq D)
    依然找到 ([B,C)) 中最高点 (p),然后再找 ([C,D]) 中第一个高于 (p) 的位置 (x);再找向左第一个高于 (x) 的位置 (q),再找到 ([C,D]) 中一个高于 (q) 的位置 (y)
    可以证明,最优解最后到达的一定是 (x)(y)

    • 如果 (q<A),那么任意 ([A,B]) 中的点去到 ([C,D]) 的路径上一定至少会经过 (p)(q) 中的一个。如果经过的是 (p),下一步直接跳到 (x),否则下一步直接跳到 (y)
    • 如果 (Aleq qleq B),那么最优解就是从 (q) 直接跳到 (y)
    • 如果 (q>B),那么 ([B,C)) 中的最高点应该是 (q) 而非 (p),矛盾。

    所以这样可能的终点就只有 (2) 个,用 (C=D) 的方法分别求出来后取较小值即可。
    时间复杂度 (O(nlog n))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=200010,LG=18;
    int n,Q,a[N],f[N][LG+1],g[N][LG+1],h[N][LG+1],mx[N][LG+1],lg[N];
    stack<int> st;
    
    void init(int N, vector<int> H)
    {
    	n=N;
    	for (int i=1;i<=n;i++)
    		a[i]=H[i-1],mx[i][0]=i;
    	for (int i=1;i<=n;i++)
    	{
    		while (st.size() && a[st.top()]<a[i]) st.pop();
    		if (st.size()) h[i][0]=st.top();
    		st.push(i);
    	}
    	while (st.size()) st.pop();
    	for (int i=n;i>=1;i--)
    	{
    		while (st.size() && a[st.top()]<a[i]) st.pop();
    		if (st.size()) g[i][0]=st.top();
    		st.push(i);
    	}
    	for (int i=1;i<=n;i++)
    	{
    		f[i][0]=(a[h[i][0]]>a[g[i][0]]) ? h[i][0] : g[i][0];
    		if (i>1) lg[i]=lg[i>>1]+1;
    	}
    	for (int j=1;j<=LG;j++)
    		for (int i=1;i<=n;i++)
    		{
    			f[i][j]=f[f[i][j-1]][j-1];
    			g[i][j]=g[g[i][j-1]][j-1];
    			h[i][j]=h[h[i][j-1]][j-1];
    			if (i+(1<<j)-1<=n)
    				mx[i][j]=(a[mx[i][j-1]]>a[mx[i+(1<<j-1)][j-1]]) ? mx[i][j-1] : mx[i+(1<<j-1)][j-1];
    		}
    }
    
    int findmax(int l,int r)
    {
    	int k=lg[r-l+1];
    	return (a[mx[l][k]]>a[mx[r-(1<<k)+1][k]]) ? mx[l][k] : mx[r-(1<<k)+1][k];
    }
    
    int binary(int l,int r,int x)
    {
    	if (r<=x)
    	{
    		for (int i=LG;i>=0;i--)
    			if (h[x][i] && h[x][i]>r) x=h[x][i];
    		if (h[x][0]>=l) return h[x][0];
    	}
    	else
    	{
    		for (int i=LG;i>=0;i--)
    			if (g[x][i] && g[x][i]<l) x=g[x][i];
    		if (g[x][0]<=r) return g[x][0];
    	}
    	return 0;
    }
    
    int query(int l,int r,int k)
    {
    	if (!k) return -1;
    	int p=h[k][0];
    	if (p>r) return -1;
    	int q=findmax(max(p+1,l),r),ans=0;
    	for (int i=LG;i>=0;i--)
    		if (f[q][i] && a[f[q][i]]<=a[k])
    			q=f[q][i],ans+=(1<<i);
    	for (int i=LG;i>=0;i--)
    		if (g[q][i] && a[g[q][i]]<=a[k])
    			q=g[q][i],ans+=(1<<i);
    	return ans;
    }
    
    int minimum_jumps(int A, int B, int C, int D)
    {
    	A++; B++; C++; D++;
    	int p=findmax(B,C-1),x=binary(C,D,p);
    	int q=binary(1,B,x),y=binary(C,D,q);
    	int c1=query(A,B,x),c2=query(A,B,y);
    	if (c1==-1) return c2;
    	if (c2==-1) return c1;
    	return min(c1,c2);
    }
    /*
    int main()
    {
    	int N, Q;
    	assert(2 == scanf("%d %d", &N, &Q));
    	std::vector<int> H(N);
    	for (int i = 0; i < N; ++i)
    		assert(1 == scanf("%d", &H[i]));
    	init(N, H);
    	for (int i = 0; i < Q; ++i)
    	{
    		int A, B, C, D;
    		assert(4 == scanf("%d %d %d %d", &A, &B, &C, &D));
    		printf("%d
    ", minimum_jumps(A, B, C, D));
    	}
    	return 0;
    }
    */
    
  • 相关阅读:
    实验10 指针2。
    作业5 指针应用1。
    实验9 指针1。
    作业4 函数应用。
    实验8 数组2。
    实验7 综合练习。
    实验6 数组1。
    实验5 函数。
    作业3 应用分支与循环结构解决问题。
    作业2 分支、循环结构。
  • 原文地址:https://www.cnblogs.com/stoorz/p/14888819.html
Copyright © 2011-2022 走看看