*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. vSphere 7.0升级至8.0-vCenter 升级

    近期,vSphere 即将发布新的正式版本,同时 vSphere 7.0 也将于 2025 年 10 月 2 日正式结束生命周期(EOS).届时,博通(Broadcom)将不再为该版本提供技术支持和更 ...

  2. GIM发布新版本了 (附rust CLI制作brew bottle流程)

    GIM 发布新版本了!现在1.3.0版本可用了 https://github.com/davelet/git-intelligence-message/releases/tag/v1.3.0 .可以通 ...

  3. C++函数重载的一点问题

    问题 #include <iostream> #include <vector> enum A { Value = 1 }; void a(std::vector<int ...

  4. 字符型union注入

    注入目标和思路:拿到库名---拿到表名---拿到列名---拿到用户名和密码 用 id=1' order by x --+ 来确定表有几列,然后用 id=0' union select x1,x1,x3 ...

  5. Manacher例题问题汇总

    Manacher例题问题汇总 本篇随笔面向个人 本来以为回文串很简单,但是没有做对应的练习前下此定论为时过早. https://www.ybtoj.com.cn/contest/75 模板 虽然例题中 ...

  6. 垃圾qt,毁我青春

    一.前言说明 在软件开发的漫漫征途中,我与 Qt 的邂逅本以为是一场美妙的合作,然而,现实却给了我沉重的一击."垃圾 qt,毁我青春",这句看似过激的话语,实则饱含了我在 Qt 开 ...

  7. 全面剖析PHP8新特性:JIT编译器如何推动网站性能革命

    本文由 ChatMoney团队出品 在Web开发领域,提高网站的响应速度一直是开发者和企业所追求的目标.随着技术的不断进步,PHP8的发布为我们带来了一个全新的工具--JIT(Just-In-Time ...

  8. 鸿蒙运动项目开发:封装超级好用的 RCP 网络库(上)—— 请求参数封装,类型转化器与日志记录篇

    鸿蒙核心技术##运动开发## Remote Communication Kit(远场通信服务) 在鸿蒙运动项目开发中,网络通信是不可或缺的一部分.无论是获取运动数据.同步用户信息,还是加载运动视频资源 ...

  9. DTALK直播预约 | 12月8日开播:后疫情时代,制造企业如何实现数字化转型?

    我国制造业拥有31个大类.179个中类和609个小类,是全球产业门类最齐全.产业体系最完整的制造业.二十大报告中强调:"坚持把发展经济的着力点放在实体经济上""推动制造业 ...

  10. 支付宝小程序IDE版本迭代异常

    前情 uni-app是我比较喜欢的跨平台框架,它能开发小程序/H5/APP(安卓/iOS),重要的是对前端开发友好,自带的IDE让开发体验也挺棒的,公司项目就是主推uni-app 现公司今年准备新开一 ...