CF1659 D Reverse Sort Sum 樹狀陣列
阿新 • • 發佈:2022-04-18
分析
設陣列的長度為 \(n\),陣列 \(C\) 中所有元素之和為 \(sum\),每個陣列中 \(1\) 的數量為 \(k\)。易知 \(k = \dfrac {sum} n\)。
由定義,陣列 \(B_i\) 的前 \(i\) 項是嚴格非遞減的。再考慮到 \(c_i = \sum_{j = 1}^n B_{j, i}\)。可以發現當 \(i > 1\) 時
\[a_i = \begin{cases} 1, & \sum_{j = 1}^{i - 1} B_{j, i} = i - 1\\ 0, & \sum_{j = 1}^{i - 1} B_{j, i} = 0 \end{cases} \]至於 \(a_1\)
由上式可以發現,針對每個 \(a_i\),我們只需要知道陣列 \(B_1, B_2, \dots,B_{i - 1}\) 的前 \(i\) 項的值即可。
因為已知的是 \(B_1 \sim B_n\) 每一項的和,可以逆序依次求出 \(a_n, a_{n - 1}, \dots\)。
首先求 \(a_n\),只要知道 \(B_1, B_2, \dots,B_{n - 1}\) 中的第 \(n\) 項的值,而可以確定的 \(B_n\) 顯然在後續的計算中會產生影響,需要減去 \(C\) 中相應的值。定義此時 \(a\) 中還未確定的數中 \(1\) 的個數為 \(x\)
類似的,接下來逐個求 \(a_i\) 的值也是類似的步驟。令此時 \(a\) 中還未確定的數中 \(1\) 的個數為 \(x\),也就是說在區間 \([1, i]\) 中 \(1\) 的個數為 \(x\)。可知 \(B_{i}\) 中的前 \(i\) 項的後 \(x\)
其中對陣列 \(C\) 實現區間修改,單點查詢用樹狀陣列維護一下就行。複雜度 \(O(N\log N)\)。
程式碼
#include <bits/stdc++.h>
#define rep(i, a, b) for (ll i = ll(a); i <= ll(b); ++i)
using namespace std;
using ll = long long;
const int N = 2e5 + 10;
int n, a[N], c[N];
int tr[N];
inline int lowbit(int x) {
return x & (-x);
}
void update(int x, int d) {
while (x <= n) {
tr[x] += d;
x += lowbit(x);
}
}
int query(int x) {
int res = 0;
while (x > 0) {
res += tr[x];
x -= lowbit(x);
}return res;
}
void solve() {
cin >> n;
ll sum = 0; // 注意陣列c中所有元素之和超過了int的範圍
rep (i, 1, n) {
cin >> c[i]; sum += c[i];
}
int k = sum / n;
int x = k;
memset(tr, 0, sizeof tr);
for (int i = 1; i <= n; i++) {
update(i, c[i] - c[i - 1]); // 對陣列c的差分陣列建樹狀陣列,用來進行區間修改
}
for (int i = n; i >= 2; i--) { // 從n到2依次確定a_i的值
if (x > 0) {
int l = i + 1 - x, r = i; // 將區間[i + 1 - x, i] 減一
update(l, -1);
update(r + 1, 1);
}
int num = query(i); // 查詢當前 c_i的值,如果為0,則a_i = 0,否則為1
if (num == 0) a[i] = 0;
else a[i] = 1, x--;
}
a[1] = x; // 最後剩餘的x即為a_1
rep (i, 1, n) cout << a[i] << ' ';
cout << '\n';
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int _; cin >> _;
while (_--) {
solve();
}
return 0;
}