本文涉及知识点
数学 构造
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 x≤10;
- 对于 60 % 60\% 60% 的评测用例, x ≤ 100 x \leq 100 x≤100;
- 对于所有评测用例, 1 ≤ x ≤ 10 9 1 \leq x \leq 10^9 1≤x≤109。
数量 逆序对
性质一:长度为n的数组,逆序最多
m
n
=
n
×
(
n
−
1
)
2
mn=\frac{n \times (n-1)}{2}
mn=2n×(n−1),整个数组逆序。
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=mni−1+n−1
性质二:存在逆序对(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(n−1)×(n−1)。
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
remain∼1,remain=len-i-1
presum记录前后缀简单的逆序对。先暴力枚举,再优化。
枚举A[i]的值。
时间复杂度:O(nnn)
性质优化
l
e
n
≈
x
len \approx \sqrt x
len≈x
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×(n−1),整个数组逆序。
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=mni−1+n−1
性质二:存在逆序对(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(n−1)=n−1我们以f(n)为基础,减少y=f(n)-x。
x
≥
f
(
n
−
1
)
,故
y
∈
[
0
,
n
−
2
]
x \ge f(n-1),故y\in[0,n-2]
x≥f(n−1),故y∈[0,n−2]
A
[
0
]
≥
n
−
y
A[0] \ge n-y
A[0]≥n−y 小于此值逆序对小于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[i−1],A[i−1]=A[i−2]可以将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
654321→543211→432211→332211
性质六:
x
>
g
e
⌊
n
/
2
⌋
\large x >ge \lfloor n/2 \rfloor
x>ge⌊n/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,n−2]故
A
[
0
]
的取值范围
n
∼
2
A[0]的取值范围n \sim 2
A[0]的取值范围n∼2
如果
A
[
0
]
≥
n
2
,
y
=
n
−
A
[
0
]
A[0]\ge \frac n 2,y=n -A[0]
A[0]≥2n,y=n−A[0]从后到前,
2
个
1
,
2
个
2
⋯
2
个
y
2个1,2个2\cdots 2个y
2个1,2个2⋯2个y
1
个
y
+
1
,
1
个
y
+
2
⋯
A
[
0
]
1个y+1,1个y+2 \cdots A[0]
1个y+1,1个y+2⋯A[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]
否则,2个A[0]2个A[0]−1⋯2个1,令z=n−2A[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]+z−1⋯A[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 _DEBUG
freopen("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;
};