Codeforces-1659 D: Reverse Sort Sum
题目链接:Codeforces-1659 D
题目
题目截图
样例描述
题目大意
给定一个只包含
n
n
n 个值为
0
0
0 或
1
1
1 的数组
A
A
A。
设函数
f
(
k
,
A
)
f(k, A)
f(k,A) 代表将数组
A
A
A 的前
k
k
k 个元素进行排序,剩余
n
−
k
n-k
n−k 个元素位置不变的操作。设
B
1
,
B
2
,
⋯
B
n
B_1,B_2,\cdots B_n
B1,B2,⋯Bn 代表
f
(
1
,
A
)
,
f
(
2
,
A
)
,
⋯
,
f
(
n
,
A
)
f(1,A),f(2,A),\cdots,f(n, A)
f(1,A),f(2,A),⋯,f(n,A)。又设
C
C
C 为
B
1
,
B
2
,
⋯
,
B
n
B_1,B_2,\cdots,B_n
B1,B2,⋯,Bn 的对应位置相加。现给出
C
C
C,问原始的
A
A
A 是什么。(题目保证
A
A
A 存在)
题目解析
我们很容易注意到,
A
n
A_n
An 的值非常好确定,因为若其为
1
1
1,那么
A
n
=
n
A_n=n
An=n,若其为
0
0
0,那么
A
n
=
1
/
0
A_n = 1/0
An=1/0(只有在全为
0
0
0 时,
A
n
A_n
An 为
0
0
0)。那么,若我们能够忽略
A
n
A_n
An 带来的影响,我们就可以用类似的方法知道
A
n
−
1
A_{n-1}
An−1 的值了。可惜,我们不能直接忽略
A
n
A_n
An 的影响,因为我们并不知道
B
n
B_n
Bn 在
B
n
−
1
B_{n-1}
Bn−1 位置上是
0
0
0 还是
1
1
1。
同时,我们应该注意到,由于操作了
n
n
n 次,
A
A
A 中每个
1
1
1 元素都会在
B
1
,
B
2
,
⋯
B
n
B_1,B_2,\cdots B_n
B1,B2,⋯Bn 中出现
n
n
n 次,也就是说,若设
C
C
C 的所有元素和为
s
u
m
sum
sum,那么
A
A
A 中
1
1
1 的数量应该为
k
=
s
u
m
n
k=\frac{sum}{n}
k=nsum。
现在我们知道了
1
1
1 的数量,我们来观察下
f
f
f 操作会产生一个怎样的序列。首先,我们将
B
B
B 序列依次横着放好(如上图 Note 中的示例)。若我们以对角线分开看这个矩阵,我们会发现第一个显然的性质,即每一行直到对角线,
1
1
1 是连续的一段(因为已经排好序)。
那么一个直观的问题变成,我们能不能知道某一行,从对角线开始,这个
1
1
1 会向左延续到哪里呢?可以!例如,在最后一行,我们知道最左边
1
1
1 的位置是
n
−
k
+
1
n-k+1
n−k+1。更一般地,若
n
−
1
n-1
n−1 位元素是
1
1
1,那么其上一行
n
−
2
n-2
n−2 行最左边
1
1
1 的位置将会不变(因为
1
1
1 的数量减少了一个),若
n
−
1
n-1
n−1 位元素是
0
0
0,显然,由于
1
1
1 的数量没变,那么对应左区间将会向左推进一位。这样,我们知道了每一行应该更新的区间,可以用树状数组或者线段树进行对应区间
C
l
⋯
i
C_{l\cdots i}
Cl⋯i 减
1
1
1,以此消除掉删除当前最后一个元素的影响,在判断时单点查询新的
C
i
C_i
Ci,这样我们就能根据第一段的方法判断每一个
A
i
A_i
Ai 具体的值了。
但我们更近一步观察,会发现整个示例有很多以对角线为斜边的等腰直角三角形。这是因为若我们竖着看,当已经排序过的位置,某一行值开始为
0
0
0 时,之后的每一行,其值都为
0
0
0(因为从上向下,每多一个
1
1
1,完成排序的位置数量也会多一个,每多一个
0
0
0,
1
1
1 的左端点会向右移),同时,我们知道若横着连续的
1
1
1 和对应竖着的应该是等长的(因为每向上一行,左端点最多向左一个单位)。那么,我们设某一左端点位置
l
l
l 对角线之下的
1
1
1 的个数为
h
[
l
]
h[l]
h[l],当遍历到位置
i
i
i 时,我们可以直接得到当前连续
1
1
1 的左端点(通过
A
i
A_i
Ai 是
0
/
1
0/1
0/1 来判断当前的
l
l
l 该向左还是不变)。这样,我们就能在倒着遍历的同时,顺带知道该位置对角线下
1
1
1 的数量是多少了。于是我们可以直接根据
C
i
−
h
i
C_i-h_i
Ci−hi 是否等于
i
i
i 来进行判断
A
i
A_i
Ai 的值。(原理类似于直线段右端沿着和它呈
45
°
45°
45° 的斜线,位置为
0
0
0 向左上平移,位置为
1
1
1 则直接向上,且线段长度减
1
1
1)
需要注意的是,在求和处需要做 long long 处理,以及,有可能
A
i
A_i
Ai 的前几位都是
0
0
0,到表现为
l
>
i
l > i
l>i(没有从对角线向左延续的
1
1
1)。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2e5 + 7;
int c[maxn], ans[maxn], h[maxn];
int main() {
int t, n, k, shift, l;
LL sum = 0;
cin >> t;
while(t--) {
sum = 0;
cin >> n;
for(int i=1; i<=n; ++i)
cin >> c[i], sum += c[i], ans[i] = 0;
k = sum / n, l = n - k + 1;
for(int i=l; i<=n; ++i) h[i] = n - i;
for(int i=n; i>=1 && l<=i; --i) {
shift = c[i] - h[i];
if(shift == i) ans[i] = 1;
else {
ans[i] = 0, --l;
h[l] = i - l - 1;
};
}
for(int i=1; i<=n; ++i)
cout << ans[i] << (i==n?'\n':' ');
}
return 0;
}