link
思维
题意
A为一个长度为
n
n
n ,仅由0,1组成的数组,设
B
i
B_i
Bi 为把
A
A
A 的前
i
i
i 项从小到大排序,其余项不变得到的数组。设数组
C
=
Σ
i
=
1
n
B
i
C = \Sigma_{i=1}^n B_i
C=Σi=1nBi
例
如
A
=
[
0
,
1
,
0
,
1
]
,
则
B
1
=
[
0
,
1
,
0
,
1
]
,
B
2
=
[
0
,
1
,
0
,
1
]
,
B
3
=
[
0
,
0
,
1
,
1
]
,
B
4
=
[
0
,
0
,
1
,
1
]
C
=
B
1
+
B
2
+
B
3
+
B
4
=
[
0
,
1
,
0
,
1
]
+
[
0
,
1
,
0
,
1
]
+
[
0
,
0
,
1
,
1
]
+
[
0
,
0
,
1
,
1
]
=
[
0
,
2
,
2
,
4
]
.
例如A=[0,1,0,1],则 B_1=[0,1,0,1], B_2=[0,1,0,1], B_3=[0,0,1,1], B_4=[0,0,1,1] C=B_1+B_2+B_3+B_4=[0,1,0,1]+[0,1,0,1]+[0,0,1,1]+[0,0,1,1]=[0,2,2,4].
例如A=[0,1,0,1],则B1=[0,1,0,1],B2=[0,1,0,1],B3=[0,0,1,1],B4=[0,0,1,1]C=B1+B2+B3+B4=[0,1,0,1]+[0,1,0,1]+[0,0,1,1]+[0,0,1,1]=[0,2,2,4].
现给出数组
C
C
C ,求数组
A
A
A。保证一定有解。
思路
并不会做,tutorial是从后往前推的,网上看到的做法觉得很巧妙。
首先初始化数组A均为1。
- 很关键的一个性质,就是在排序的过程中,若 j j j 已经被排序了,则这个位置上的数字不会变大。 这里用 A [ i , j ] A[i,j] A[i,j] 表示对前 i i i 项排序时第 j j j 项的值。则若 A [ i , j ] = 0 ( j ≤ i ) A[i, j] = 0(j \leq i) A[i,j]=0(j≤i),则 A [ i + 1 , j ] . . . A [ n , j ] = 0 A[i+1,j]...A[n, j] = 0 A[i+1,j]...A[n,j]=0 ,证明略。
- 我们再看
C
[
i
]
C[i]
C[i], 这里
C
[
i
]
C[i]
C[i] 表示C的第
i
i
i 项。它由两部分组成,前
i
−
1
i-1
i−1 次排序时的值(A[i])和后
n
−
i
+
1
n-i+1
n−i+1 次排序的值(若干个1,0)。 令
c[i] = c[i] - (i-1) * a[i]
, 此时 C [ i ] C[i] C[i] 就是后 n − i + 1 n-i+1 n−i+1 次排序的和。由1,这 n − i + 1 n-i+1 n−i+1 个值一定是形如 111000的格式(若干个1之后若干个0)。也就是 i i i 之后有 C [ i ] C[i] C[i] 个1,故 a [ i + c [ i ] ] = 0 a[i+c[i]] = 0 a[i+c[i]]=0,从前往后遍历即可更新 a [ i ] a[i] a[i]。 - 上述更新方法对于第 i i i 位,我们都能找到第 i i i 个0的位置
- 注意要先找到第一个不为0的点,因为前缀0没有影响到后面。
代码
int c[maxn];
int a[maxn];
void solve() {
int n;
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> c[i];
a[i] = 1;
}
int st = 1;
while(!c[st]) {
a[st] = 0;
st++;
}
for(int i = st; i <= n; i++) {
if(c[i] == n) break;
int t;
if(a[i])
t = c[i] - (i - 1);
else
t = c[i];
if(t + i > n) continue; //防止越界
a[t + i] = 0;
}
for(int i = 1; i <= n; i++) {
cout << a[i] << ' ';
}
cout << endl;
}