*CF1827B2 Range Sorting (Hard Version)

一.思路:

按题意去找似乎很麻烦。但是注意到,我们若对每个区间都暴力的花费\(r-l\)的代价去修改所有区间,总代价为

\[\sum_{l=1}^{n}\sum_{r=l}^n(r-l)=\frac{1}{2}\sum_{l=1}^{n}(n-l+1)\times(n-l)=\frac{n(n+1)(n-1)}{6}
\]

这就提醒我们反向来思考,发现我们如果将一段区间拆成两段处理,并且依旧可以变得有序,代价会少\(1\),而且两次操作区间与区间之间不相交一定最优,所以我们的目标就是要找到所有可以这样拆开的区间来减少我们花费的代价。可以证明的是,满足这样的条件的区间需要满足的充要条件是:

\[\exists x\in[l+1,r],\max_{k=l}^{x-1}a_k<\min_{k=x}^{r}a_k
\]

即前半段最大值小于后半段最小值。

我们发现直接模拟这个性质复杂度属于是直接爆了,所以换个思路,我们假定某一个位置\(i\),\(a_i\)是某个右半区间\([x,r]\)的最小值,那么\([l,x-1]\)的最大值要小于\(a_i\)

现在也就需要统计有多少区间\([l,r]\)可以拆成两半,并且一定有\(i\in[x,r]\)

那么我们找到\(i\)后面第一个比\(i\)小的数\(a_{minr}\),所以\(r\)可以是区间\([x,minr)\)中的任意整数。接着我们找到\(i\)前面第一个比\(a_i\)小的数\(a_{tmin}\),根据定义,这个数只能放在左半边区间,且作为左右区间分界点,也就是下标\(tmin=x-1\)。最后,从\(tmin\)往前找第一个比\(a_i\)大的数\(a_{maxl}\),所以\(l\)可以是区间\((maxl,tmin]\)中的任意一个数。

因为每拆一次少\(1\)的代价,所以最后来自这些区间的贡献就为:

\[(minr-i)\times(tmin-maxl)
\]

现在有一个关键问题:如何快速处理找前后的第一个\(\max/\min\)呢?其实处理方式有很多,这里说一下我个人的方法:相信找数这个问题我们都不陌生,很容易想到二分查找,但是问题在于这道题的序列是无序的,没有单调性,所以我们理应要去构造一些新东西来给二分check。可能没什么头绪,那我们不妨从头思考,为什么我们的复杂度高呢?显然的,这是因为我们每一次都要去扫描前面的数然后去打擂台处理,我们希望有一种办法可以快速高效的查询一段区间的最值,所以第一反应是线段树或者树状数组,事实上,这是可行的,但是别忘了,我们的序列是静态的,我们有更加快速的查询方法,即:我会ST表!我们尝试着将ST表与二分进行结合,每一次查询起始点到\(mid\)之间d的最值,结合当前的\(a_i\)的大小对数级别复杂度缩小每一次的查询范围,最后得到结果

二.code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
inline LL read() {
int x=0,f=1;char ch=getchar();
while(ch > '9' || ch < '0'){if(ch == '-'){f = -1;}ch = getchar();}
while(ch >= '0'&&ch <= '9'){x = x * 10 + ch - 48; ch = getchar();}
return x * f;
} const int N = 1e6 + 10;
const int LOg_2 = 20;
LL n,ans;
int perm[N],st_min[N][LOg_2],st_max[N][LOg_2];
int l2[N]; void init()
{
int lim = l2[n];
for(int j = 1;j <= lim;++j)
{
int add_up = (1 << j) - 1,add_up2 = (1 << (j - 1));
for(int i = 1;i + add_up - 1 <= n;++i)
{
st_min[i][j] = std::min(st_min[i][j - 1],st_min[i + add_up2][j - 1]);
st_max[i][j] = std::max(st_max[i][j - 1],st_max[i + add_up2][j - 1]);
}
}
} int query_min(int l,int r)
{
int k = l2[r - l + 1];
return std::min(st_min[l][k],st_min[r - (1 << k) + 1][k]);
} int query_max(int l,int r)
{
int k = l2[r - l + 1];
return std::max(st_max[l][k],st_max[r - (1 << k) + 1][k]);
} int main()
{
n = read();
for(int i = 1;i <= n;++i)
{
perm[i] = read();
st_min[i][0] = st_max[i][0] = perm[i];
}
ans = (n * (n + 1) / 2) * (n - 1) / 3;
l2[0] = -1;
for(int i = 1;i <= n;++i) l2[i] = l2[i >> 1] + 1;
init(); for(int i = 1;i <= n;++i)
{
int ll = 0,rl = 0,rr = n + 1;
int l = i + 1,r = n;
while(l <= r)
{
int mid = (l + r) >> 1;
if(query_min(i + 1,mid) < perm[i])
r = mid - 1,rr = mid;
else
l = mid + 1;
}
l = 1,r = i - 1;
while(l <= r)
{
int mid = (l + r) >> 1;
if(query_min(mid,i - 1) < perm[i])
l = mid + 1,rl = gmid;
else
r = mid - 1;
}
l = 1,r = rl - 1;
while(l <= r)
{
int mid = (l + r) >> 1;
if(query_max(mid,rl - 1) > perm[i])
l = mid + 1,ll = mid;
else
r = mid - 1;
}
ans -= 1ll * (rr - i) * (rl - ll);
}
std::cout << ans;
return 0;
}

CF1827B2 Range Sorting (Hard Version) 题解的更多相关文章

  1. Codeforces 258D Little Elephant and Broken Sorting (看题解) 概率dp

    Little Elephant and Broken Sorting 怎么感觉这个状态好难想到啊.. dp[ i ][ j ]表示第 i 个数字比第 j 个数字大的概率.转移好像比较显然. #incl ...

  2. Codeforces 1196D2 RGB Substring (Hard version) 题解

    题面 \(q\) 个询问,每个询问给出一个字符串 \(s\),要你在 \(s\) 中用最小替换得到无穷字符串 RGBRGBRGB... 的长度为定值 \(k\) 的子串. 题解 一眼看过去可能是编辑距 ...

  3. [LeetCode] Range Sum Query - Mutable 题解

    题目 题目 思路 一看就是单点更新和区间求和,故用线段树做. 一开始没搞清楚,题目给定的i是从0开始还是从1开始,还以为是从1开始,导致后面把下标都改掉了,还有用区间更新的代码去实现单点更新,虽然两者 ...

  4. CF1157C1-Increasing Subsequence (easy version)题解

    原题地址 题目大意:

  5. Hdoj 2501.Tiling_easy version 题解

    Problem Description 有一个大小是 2 x n 的网格,现在需要用2种规格的骨牌铺满,骨牌规格分别是 2 x 1 和 2 x 2,请计算一共有多少种铺设的方法. Input 输入的第 ...

  6. Palindromes _easy version 题解

    “回文串”是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就是回文串.请写一个程序判断读入的字符串是否是“回文”. Input输入包含多个测试实例,输入数据的第一行是一个正整数n ...

  7. CF1203D2 Remove the Substring (hard version) 题解

    这题初赛让我白给了6分,于是我决定回来解决一下它. 说实话,看原题题面和看CCF代码真是两种完全不同的感受…… ------------思路分析: 把$s$串删去一部分之后,会把$s$串分成两部分,当 ...

  8. CF605A Sorting Railway Cars 题解

    To CF 这道题依题意可得最终答案为序列长度-最长子序列长度. 数据范围至 \(100000\) 用 \(O(n^2)\) 的复杂度肯定会炸. 用 \(O(nlogn)\) 的复杂度却在第 \(21 ...

  9. UVA12558 Egyptian Fractions (HARD version) (埃及分数,迭代加深搜索)

    UVA12558 Egyptian Fractions (HARD version) 题解 迭代加深搜索,适用于无上界的搜索.每次在一个限定范围中搜索,如果无解再进一步扩大查找范围. 本题中没有分数个 ...

  10. [Leetcode Week16]Range Sum Query - Mutable

    Range Sum Query - Mutable 题解 原创文章,拒绝转载 题目来源:https://leetcode.com/problems/range-sum-query-mutable/de ...

随机推荐

  1. odoo备份数据库无法还原问题解决:Command 'pg_dump' not found.

    背景景:ubuntu20.04 上用命令安装postgresql后,odoo备份数据库报如下错误 安装命令:sudo apt-get install postgresql 默认安装:14版本的pg 错 ...

  2. Vite代理配置不生效问题

    1.问题: 在写Vite+vue3.0项目时,配置vite代理,遇到不起效的问题,具体如下: // vite.config.ts proxy: { '/api': ' http://localhost ...

  3. 树结构Tree

    树结构 平衡顺序二叉树 通过平衡顺序二叉树查数据的时候,也就等同于二分查找的操作 Binary Search 2,3树 二三树 二三树有 node2 和 node3 两种节点,树的规则如图 2-3-4 ...

  4. Vue 注意事项

    Top 1 v-once:标签的内容只改变一次: <span v-once>这个将不会改变: {{ msg }}</span> Top 2 v-html:将内容以HTML格式输 ...

  5. 最值得关注的2025年公众号编辑器Top 10,提升你的内容质量

    公众号编辑器全面指南:10款主流工具深度评测 前言 公众号编辑器作为新媒体和自媒体人日常必备的工具,能极大提升公众号运营效率和内容质量.面对众多类型和风格各异的公众号编辑器网站,如何选择一款真正适合自 ...

  6. 渗透中的逆向工具-jsrpc实操手记

    前言 在渗透测试过程中,有些网站的接口参数是加密的.对于逆向小菜鸡的我来说,遇到这种网站总是束手无策,不能修改其中的参数,也就无法进行下一步的测试.偶然间发现一款js逆向工具jsrpc,它可以直接调用 ...

  7. C#/.NET/.NET Core技术前沿周刊 | 第 43 期(2025年6.16-6.22)

    前言 C#/.NET/.NET Core技术前沿周刊,你的每周技术指南针!记录.追踪C#/.NET/.NET Core领域.生态的每周最新.最实用.最有价值的技术文章.社区动态.优质项目和学习资源等. ...

  8. MySQL索引完全指南:让你的查询速度飞起来

    MySQL索引完全指南:让你的查询速度飞起来 还在为数据库查询慢而头疼吗?一个简单的索引就能让你的查询速度提升几十倍甚至上百倍!今天我将用最通俗易懂的方式,带你彻底搞懂MySQL索引的奥秘.从什么是索 ...

  9. 新人如何入门学习 STM32?

    作为一个在嵌入式领域摸爬滚打了近10年的老兵,看到这个问题时我的思绪瞬间回到了当年那个懵懂的自己.说实话,2014年那个夏天,24岁的我刚从机械专业毕业却被调剂到了厦门某马的电子部门,第一次听到&qu ...

  10. Vite加Vue3加Ts创建项目一些问题汇总

    版权声明:本文为CSDN博主「一尾流莺_」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明.原文链接:https://blog.csdn.net/m0_48721669 ...