当前位置: 首页 > news >正文

【数学 逆序对 构造】P12386 [蓝桥杯 2023 省 Python B] 混乱的数组|普及+

本文涉及知识点

数学 构造

P12386 [蓝桥杯 2023 省 Python B] 混乱的数组

题目描述

给定一个正整数 x x x,请找出一个尽可能短的仅含正整数的数组 A A A 使得 A A A 中恰好有 x x x i , j i, j i,j 满足 i < j i < j i<j A i > A j A_i > A_j Ai>Aj

如果存在多个这样的数组,请输出字典序最小的那个。

输入格式

输入一行包含一个整数表示 x x x

输出格式

输出两行。

第一行包含一个整数 n n n,表示所求出的数组长度。

第二行包含 n n n 个整数 A i A_i Ai,相邻整数之间使用一个空格分隔,依次表示数组中的每个数。

输入输出样例 #1

输入 #1

3

输出 #1

3
3 2 1

说明/提示

评测用例规模与约定

  • 对于 30 % 30\% 30% 的评测用例, x ≤ 10 x \leq 10 x10
  • 对于 60 % 60\% 60% 的评测用例, x ≤ 100 x \leq 100 x100
  • 对于所有评测用例, 1 ≤ x ≤ 10 9 1 \leq x \leq 10^9 1x109

数量 逆序对

性质一:长度为n的数组,逆序最多 m n = n × ( n − 1 ) 2 mn=\frac{n \times (n-1)}{2} mn=2n×(n1),整个数组逆序。 m n 0 = 0 , m n i = m n i − 1 + n − 1 mn_0=0,mn_i=mn_{i-1}+n-1 mn0=0,mni=mni1+n1
性质二:存在逆序对(i,j),则一定存在相邻逆序对。如果{i,i+1},{i+1,i+2} ⋯ \cdots {j-1,j}都不是逆序对,则(i,j)也不是逆序对。
性质三:1到n排列,通过调整顺序,可以让逆序对为[0,mn]的任意数。选择任意相邻逆序对交换,逆序对减1。
性质四:长度为n的数字,x1是A[0]和其它数字的逆序对数量;x2是A[1…n-1]之间的逆序对数量。x2一定是 ( n − 1 ) × ( n − 1 ) 2 \frac{(n-1)\times (n-1)}{2} 2(n1)×(n1)
A[0]=A[1…N-1]最小的x1个数的最大值+1。 x2++,有如下方式:a,交换顺序。b,某个数变小。c,某个数变大。x2++,意味着x1–。
无论那种方式A[0]只会变小或不变,不会变大。故A[1…N-1] 是N-1到1的逆序。
通过i从0到N-1枚举A[i],A[0…i-1]已经确定, A[i+1…] 是[1,N-i+1]的逆序。

细节

数组分三部分
前缀A[0…i-1]:内部的逆序对直接从x减掉。cnt[i]记录前缀中大于等于i的数量
当前A[i]。
后缀:一定是 r e m a i n ∼ 1 remain \sim 1 remain1,remain=len-i-1
presum记录前后缀简单的逆序对。先暴力枚举,再优化。
枚举A[i]的值。
时间复杂度:O(nnn)

性质优化

l e n ≈ x len \approx \sqrt x lenx
cnt[i]记录前缀中大于等于i的数量。对应差分数组是diff。则A[i]选择为x,则cnt[x…len]++,即diff[x]++。
这样可以在 log ⁡ l e n \log len loglen的时间内,区间修改cnt,单点查询。cnt[cur+1]就是当前值选择cur,当前和前缀的逆序对。
[1…remain]和前缀构成的逆序对数量:
cnt2[i] = cnt[i]*i
则后缀和前缀组成的逆序对:
c n t 2 [ c u r + 1.... ] − c n t [ c u r + 1... ] ∗ c u r cnt2[cur+1....]-cnt[cur+1...]*cur cnt2[cur+1....]cnt[cur+1...]cur
cnt2也用树状数组实现。
时间复杂度 O ( l e n l o g l e n l o g l e n ) O(len loglen loglen) O(lenloglenloglen)

此算法错误

以上性质,只有 i = = 0 i==0 i==0是成立。比如:111111?11 优化111111?21

新解法

性质一 A [ 0 ] ≠ 1 A[0]\neq1 A[0]=1,否则A[0]为0,不会和任意数产生逆序对。直接删除,更短。
** 性质二**:如果 A [ i ] ≥ A [ i + 1 ] A[i] \ge A[i+1] A[i]A[i+1]则所有 A [ j ] ≤ A [ j + 1 ] , i + 1 < j A[j] \le A[j+1],i+1<j A[j]A[j+1],i+1<j。假定 A [ i 1 ] ≤ A [ i 1 + 1 ] A[i1]\le A[i1+1] A[i1]A[i1+1]不会成立。则交换 A [ i ] , s [ i + 1 ] A[i],s[i+1] A[i],s[i+1]逆序减1,交换A[j]和s[j+1]逆序对+1 。逆序对不变,字典序变小。
性质三 s [ i ] > s [ i + 1 ] < s [ i + 2 ] s[i]> s[i+1] < s[i+2] s[i]>s[i+1]<s[i+2]不成立。
情况a:a[i] > s[i+2]。如:312。321,逆序对++;231,逆序对–。逆序数不变,字典序更小。
情况b,a[i] < s[i+2]。如:213。123,逆序对–。132,逆序对++。逆序数不变,字典序更小。
情况c, s [ i ] = = s [ i + 2 ] s[i]==s[i+2] s[i]==s[i+2]如:212。 ????待证。
结论一:如果s[i]<s[i+1],s[0…i]严格不减。
性质四 A [ 0 ] < A [ 1 ] < A [ 2 ] A[0] <A[1] <A[2] A[0]<A[1]<A[2]

正确解法

从新开始。
性质一:长度为n的数组,逆序最多 m n = n × ( n − 1 ) 2 mn=\frac{n \times (n-1)}{2} mn=2n×(n1),整个数组逆序。 m n 0 = 0 , m n i = m n i − 1 + n − 1 mn_0=0,mn_i=mn_{i-1}+n-1 mn0=0,mni=mni1+n1
性质二:存在逆序对(i,j),则一定存在相邻逆序对。如果{i,i+1},{i+1,i+2} ⋯ \cdots {j-1,j}都不是逆序对,则(i,j)也不是逆序对。
用f(i)代替 m m i mm_i mmi
求最小f(n) >= x。
性质三:如果 x = = f ( n ) x == f(n) x==f(n)。1到n的全排列逆序。
性质四:无论是否有重复的数字,逆序能得到最多的逆序对。存在一组重复2次的数字,逆序数减1。一组重复3次的数组,逆序对减少3。存在一组重复m次的数字,逆序对减少m-1次。
性质五 f ( n ) − f ( n − 1 ) = n − 1 f(n)-f(n-1)=n-1 f(n)f(n1)=n1我们以f(n)为基础,减少y=f(n)-x。 x ≥ f ( n − 1 ) ,故 y ∈ [ 0 , n − 2 ] x \ge f(n-1),故y\in[0,n-2] xf(n1),故y[0,n2]
A [ 0 ] ≥ n − y A[0] \ge n-y A[0]ny 小于此值逆序对小于x。降低A[i]的值到<A[0],(A[0],A[i])成为新逆序对。根根据性质四,A[1…n]的逆序对减1。
推论:A降序,如果 A [ i ] = = A [ i − 1 ] , A [ i − 1 ] ≠ A [ i − 2 ] A[i]==A[i-1],A[i-1]\neq A[i-2] A[i]==A[i1]A[i1]=A[i2]可以将A[i]赋值为A[i-2],逆序对不变。字典序变小。

x=10为例:5 4 3 2 1。
x=9:4 3 2 1 1,逆序对减1。
x=8:3 2 2 1 1,逆序对减1。
如果n是奇数,可以减少 ⌊ n / 2 ⌋ \lfloor n/2 \rfloor n/2
如果是偶数,可以减少 n / 2 n/2 n/2
以x=15为例:
654321 → 543211 → 432211 → 332211 654321\rightarrow 543211 \rightarrow 432211 \rightarrow332211 654321543211432211332211
性质六: x > g e ⌊ n / 2 ⌋ \large x >ge \lfloor n/2 \rfloor x>gen/2
x = 23为例。
24: 4 4 3 3 2 2 1 1
将A[0]改成3,变成 34332211 3 4 3 3 2 2 11 34332211
3 4 3 3 ,逆序对减少2。A[1…n]已经是逆序,无法通过调整顺序增加逆序,只能减少重复数字。即:3433变成543。
即:**:23: 3 5 4 3 2 2 1 1
分析:x = 22
A[0]改成2,后面的2个2重复,减少2个逆序对。增加一个逆序只能:
5432全部+1,减少一个重复。
总结
A [ 0 ] = n − ( f ( n ) − x ) , f ( n ) − x 的取值范围 [ 0 , n − 2 ] A[0]=n-(f(n)-x),f(n)-x的取值范围[0,n-2] A[0]=n(f(n)x),f(n)x的取值范围[0,n2] A [ 0 ] 的取值范围 n ∼ 2 A[0]的取值范围n \sim 2 A[0]的取值范围n2
如果 A [ 0 ] ≥ n 2 , y = n − A [ 0 ] A[0]\ge \frac n 2,y=n -A[0] A[0]2n,y=nA[0]从后到前, 2 个 1 , 2 个 2 ⋯ 2 个 y 2个1,2个2\cdots 2个y 21222y 1 个 y + 1 , 1 个 y + 2 ⋯ A [ 0 ] 1个y+1,1个y+2 \cdots A[0] 1y+1,1y+2A[0]
否则, 2 个 A [ 0 ] 2 个 A [ 0 ] − 1 ⋯ 2 个 1 ,令 z = n − 2 A [ 0 ] 否则,2个A[0] 2个A[0]-1\cdots 2个1,令z = n - 2A[0] 否则,2A[0]2A[0]121,令z=n2A[0]两个A[0]之间插入 A [ 0 ] + z , A [ 0 ] + z − 1 ⋯ A [ 0 ] + 1 A[0]+z,A[0]+z-1 \cdots A[0]+1 A[0]+z,A[0]+z1A[0]+1

代码

核心代码

#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>
#include<array>#include <bitset>
using namespace std;template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {in >> pr.first >> pr.second;return in;
}template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t);return in;
}template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);return in;
}template<class T1, class T2, class T3, class T4, class T5, class T6, class T7 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4,T5,T6,T7>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t) >> get<4>(t) >> get<5>(t) >> get<6>(t);return in;
}template<class T = int>
vector<T> Read() {int n;cin >> n;vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}
template<class T = int>
vector<T> ReadNotNum() {vector<T> ret;T tmp;while (cin >> tmp) {ret.emplace_back(tmp);if ('\n' == cin.get()) { break; }}return ret;
}template<class T = int>
vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}template<int N = 1'000'000>
class COutBuff
{
public:COutBuff() {m_p = puffer;}template<class T>void write(T x) {int num[28], sp = 0;if (x < 0)*m_p++ = '-', x = -x;if (!x)*m_p++ = 48;while (x)num[++sp] = x % 10, x /= 10;while (sp)*m_p++ = num[sp--] + 48;AuotToFile();}void writestr(const char* sz) {strcpy(m_p, sz);m_p += strlen(sz);AuotToFile();}inline void write(char ch){*m_p++ = ch;AuotToFile();}inline void ToFile() {fwrite(puffer, 1, m_p - puffer, stdout);m_p = puffer;}~COutBuff() {ToFile();}
private:inline void AuotToFile() {if (m_p - puffer > N - 100) {ToFile();}}char  puffer[N], * m_p;
};template<int N = 1'000'000>
class CInBuff
{
public:inline CInBuff() {}inline CInBuff<N>& operator>>(char& ch) {FileToBuf();while (('\r' == *S) || ('\n' == *S) || (' ' == *S)) { S++; }//忽略空格和回车ch = *S++;return *this;}inline CInBuff<N>& operator>>(int& val) {FileToBuf();int x(0), f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);val = f ? -x : x; S++;//忽略空格换行		return *this;}inline CInBuff& operator>>(long long& val) {FileToBuf();long long x(0); int f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);val = f ? -x : x; S++;//忽略空格换行return *this;}template<class T1, class T2>inline CInBuff& operator>>(pair<T1, T2>& val) {*this >> val.first >> val.second;return *this;}template<class T1, class T2, class T3>inline CInBuff& operator>>(tuple<T1, T2, T3>& val) {*this >> get<0>(val) >> get<1>(val) >> get<2>(val);return *this;}template<class T1, class T2, class T3, class T4>inline CInBuff& operator>>(tuple<T1, T2, T3, T4>& val) {*this >> get<0>(val) >> get<1>(val) >> get<2>(val) >> get<3>(val);return *this;}template<class T = int>inline CInBuff& operator>>(vector<T>& val) {int n;*this >> n;val.resize(n);for (int i = 0; i < n; i++) {*this >> val[i];}return *this;}template<class T = int>vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {*this >> ret[i];}return ret;}template<class T = int>vector<T> Read() {vector<T> ret;*this >> ret;return ret;}
private:inline void FileToBuf() {const int canRead = m_iWritePos - (S - buffer);if (canRead >= 100) { return; }if (m_bFinish) { return; }for (int i = 0; i < canRead; i++){buffer[i] = S[i];//memcpy出错			}m_iWritePos = canRead;buffer[m_iWritePos] = 0;S = buffer;int readCnt = fread(buffer + m_iWritePos, 1, N - m_iWritePos, stdin);if (readCnt <= 0) { m_bFinish = true; return; }m_iWritePos += readCnt;buffer[m_iWritePos] = 0;S = buffer;}int m_iWritePos = 0; bool m_bFinish = false;char buffer[N + 10], * S = buffer;
};class Solution {
public:vector<int> Ans(int x) {vector<int> mn(1);while (mn.back() < x) {mn.emplace_back(mn.size() - 1 + mn.back());}const int len = mn.size() - 1;int  y = mn.back() - x;vector<int> ans;int a0 = len - y;if (2 * a0 >= len) {for (int i = a0; i > 0; i--) {ans.emplace_back(i);if (i <= len - a0) {ans.emplace_back(i);}}}else {for (int i = a0; i > 0; i--) {ans.emplace_back(i);ans.emplace_back(i);}vector<int> tmp;for (int i = len - 2 * a0; i > 0; i--) {tmp.emplace_back(a0 + i);}ans.insert(ans.begin() + 1, tmp.begin(), tmp.end());}return ans;}
};int main() {
#ifdef _DEBUGfreopen("a.in", "r", stdin);
#endif // DEBUG	ios::sync_with_stdio(0); cin.tie(nullptr);//CInBuff<> in; COutBuff<10'000'000> ob;	/*for (int x = 1; x <= 28; x++) {cout << x << ":";auto res = Solution().Ans(x);for (const auto& i : res) {cout << i << " ";}cout << "\n";}*/int x;cin >> x;
#ifdef _DEBUG	//printf("iH=%d,iA=%d,H=%d,dA=%d",iH,iA,H,dA);//Out(C, ",C=");//Out(edge, ",edge=");		/*Out(que, ",que=");*///Out(ab, ",ab=");//Out(par, "par=");//Out(que, "que=");//Out(B, "B=");
#endif // DEBUG	auto res = Solution().Ans(x);cout << res.size() << "\n";for (const auto& i : res) { cout << i << " "; }return 0;
};

相关文章:

  • 深度剖析:AI 建站的现状、局限与未来展望-AI编程建站实战系列预告优雅草卓伊凡
  • demo_win10配置WSL、DockerDesktop环境,本地部署Dify,ngrok公网测试
  • 什么是 CPU 缓存模型?
  • NVMe IP现状扫盲
  • azure devops 系列 - 常用的task
  • MPTCP 聚合吞吐
  • 图论刷题1
  • Qt OpenGL 实现交互功能(如鼠标、键盘操作)
  • 【Linux网络】传输层TCP协议
  • 设计模式——访问者设计模式(行为型)
  • docker运行程序Killed异常排查
  • AlmaLinux OS 10 正式发布:兼容 RHEL 10 带来多项技术革新
  • 页岩油开采的阶段
  • HealthBench医疗AI评估基准:技术路径与核心价值深度分析(下)
  • 计算机视觉---YOLOv6
  • 20250602在荣品的PRO-RK3566开发板的Android13下的uboot启动阶段配置BOOTDELAY为10s
  • neo4j 5.19.0两种基于向量进行相似度查询的方式
  • UE5 2D地图曝光太亮怎么修改
  • 从0开始学vue:pnpm怎么安装
  • 【iOS】YYModel源码解析
  • wordpress 当前页/企业seo顾问服务
  • 核工业华南建设集团网站/百度首页关键词优化
  • 深圳知名网站建设/十大免费excel网站
  • 网站开发设计语言/优化方案的格式及范文
  • 上海室内设计有限公司/潜江seo
  • 报价网站系统/网址收录