Codeforces-1696 D: Permutation Graph【构造、分治、数据结构】

Codeforces-1696 D: Permutation Graph

题目传送门:Codeforces-1696 D: Permutation Graph

题目

题目截图

在这里插入图片描述

样例描述

在这里插入图片描述
在这里插入图片描述

题目大意

  给定一个 1 ⋯ n 1 \cdots n 1n 的排列,对于一个连续的区间 a i ⋯ a j a_i \cdots a_j aiaj,若 a i a_i ai 是这段区间的最小/最大值,同时, a j a_j aj 也是这段区间的最小/最大值,那么我们可以在 i , j i, j i,j 结点上连一条无向边。问这样构造出的图,从 1 1 1 n n n 的最短路径。

题目解析

  首先暴力是肯定不行的,因为若是一个单调的数组,能连的边是 n 2 n^2 n2 级别的。
  这道题表面上是最短路径,实际上是考察必经结点。现在假设 a 1 ⋯ a n a_1 \cdots a_n a1an 的最大值在 a 2 ⋯ a n − 1 a_2 \cdots a_{n-1} a2an1 中,那么一个显然的结论是,这种情况下我们是无法绕过最大值从 1 1 1 达到 n n n 的,最小值同理。那么其中的最大值就变成了一个必经的结点 k k k。那么这样的必经之路有多少呢,显然 [ 1 , k ) [1,k) [1,k) 中的最小值, ( k , n ] (k,n] (k,n] 中的最小值都是必经之路,之后我们递归向左向右寻找这种必经之路,就能形成 1 1 1···最小-最大-最小··· n n n 这样的路径,显然,只存在必经之路的路径是最短的。

Code

#include <bits/stdc++.h>
using namespace std;


const int maxn = 6e5 + 7;
int log_2[maxn];
struct P {
    int v, id;
}mx[maxn][18], mn[maxn][18];

void setST(const vector<int>& a) {
    log_2[0] = -1;
    for(int i=0; i<a.size(); ++i) {
        mx[i][0].v = mn[i][0].v = a[i];
        mx[i][0].id = mn[i][0].id = i;
        log_2[i+1] = (((i+1)&i) == 0)?log_2[i]+1: log_2[i];
    }
    for(int j=1; j<18; ++j)
        for(int i=0; i<a.size(); ++i) {
            mx[i][j] = (mx[i][j-1].v < mx[i+(1<<(j-1))][j-1].v)? mx[i+(1<<(j-1))][j-1]:mx[i][j-1];
            mn[i][j] = (mn[i][j-1].v > mn[i+(1<<(j-1))][j-1].v)? mn[i+(1<<(j-1))][j-1]:mn[i][j-1];
        }
}

int query_mx(int l, int r) {
    int k = log_2[r - l + 1];
    if(mx[l][k].v < mx[r - (1<<k) + 1][k].v)
        return mx[r - (1<<k) + 1][k].id;
    else return mx[l][k].id;
}

int query_mn(int l, int r) {
    int k = log_2[r - l + 1];
    if(mn[l][k].v > mn[r - (1<<k) + 1][k].v)
        return mn[r - (1<<k) + 1][k].id;
    else return mn[l][k].id;
}

void dfs(int l, int r, bool cur, bool dir, vector<int>& a, vector<int>& ans) {
    if(dir) {
        int idx = cur?query_mn(l, r):query_mx(l, r);
        if(l < idx) dfs(l, idx, cur^1, dir, a, ans);
        ans.emplace_back(idx);
    } else {
        int idx = cur?query_mn(l, r):query_mx(l, r);
        if(idx < r) dfs(idx, r, cur^1, dir, a, ans);
        ans.emplace_back(idx);
    }
}

int main() {
   int T, n;
   cin >> T;
   while(T--) {
        cin >> n;
        vector<int> a(n);
        for(int& i : a) cin >> i;

        if(n == 1) {
            cout << 0 << endl; continue;
        } else if(n == 2) {
            cout << 1 << endl; continue;
        }

        setST(a);
        vector<int> ans;
        int idx = query_mx(0, n - 1); ans.emplace_back(idx);

        if(0 < idx) dfs(0, idx, true, true, a, ans);
        if(idx < n - 1) dfs(idx, n - 1, true, false, a, ans);

        cout << ans.size() - 1 << endl;
   }
   return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值