1. 程式人生 > 其它 >CF1659 D Reverse Sort Sum 樹狀陣列

CF1659 D Reverse Sort Sum 樹狀陣列

分析

設陣列的長度為 \(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\)

,也就是說 \(B_n\) 的數字 \(1\) 所在的區間為 \([n + 1 - x, n - 1]\)。所以只需要對 \(C\) 中的 \([n + 1 - x, n]\) 減一即可。此時的 \(c_n\) 就等於 \(B_1, B_2, \dots,B_{n - 1}\) 中的第 \(n\) 項的值。\(a_n\) 可以確定。

類似的,接下來逐個求 \(a_i\) 的值也是類似的步驟。令此時 \(a\) 中還未確定的數中 \(1\) 的個數為 \(x\),也就是說在區間 \([1, i]\)\(1\) 的個數為 \(x\)。可知 \(B_{i}\) 中的前 \(i\) 項的後 \(x\)

項均為 \(1\)。由於\(B_{i}\)\([i + 1, n]\) 對前 \(i\) 項的求解無關,無須考慮,只有前 \(i\) 項有影響,且其中 \(1\) 所在的區間為 \([i + 1 - x, i]\),只需要對 \(C\) 中的 \([i + 1 - x, i]\) 減一即可。可以知道此時 \(c_i = \sum_{j = 1}^{i - 1} B_{j, i}\)\(a_i\) 也可以被確定了。

其中對陣列 \(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;
}