題解 CF993E 【Nikita and Order Statistics】

EternalEpic發表於2024-09-16

初看這道題,以為又是什麼資料結構數數題,沒啥思路,結果推式子時搞出了一個類似可以卷積的玩意兒,所以果斷 \(FFT\) 解決。

那我們來分析問題:

  1. 這道題裡,值域沒用,每一個數只要管它與 \(x\) 的相對大小關係即可。如果它小於 \(x\) 那麼有貢獻,賦值為一,否則為零。然後,可以求字首和,區間部分和問題往往可以用字首和配對端點解決。

  2. \(f_i\) 表示字首和為 \(i\) 的字首端點數量,\(m\) 為總共比 \(x\) 小的數的個數。顯然的,對於恰好有大於一個數比 \(x\) 小的情況中,答案 \(g_k = \sum\limits_{i=0}^{m-k} f_i \times f_{i+k}\)。 這個式子意思就是取兩個端點使區間值符合條件,然後用加乘原理統計一下。

  3. 考慮常用技巧,變換順序,令 \(h_i = f_{m-i}\) ,所以 \(f_{i+k} = h_{m-i-k}\) ,這樣的話,\(g\) 就可以寫成兩者加法卷積了。大功告成。

  4. 不要忘了特殊處理 \(g_0\) ,經過推導可知 \(g_0 = \sum\limits_{i=0}^m \frac{f_i\times{(f_i + 1)}}{2}\) ,暴力算就好。

struct Vector {
	double x, y;
	Vector(double _x = 0, double _y = 0) : x(_x), y(_y) {}

	inline Vector Vary(void) { return Vector(x, -y); }

	inline bool operator < (const Vector&rhs)
	const { return x == rhs.x ? y < rhs.y : x < rhs.x; }
	inline Vector operator - (const Vector&rhs)
	const { return Vector(x - rhs.x, y - rhs.y); }
	inline Vector operator + (const Vector&rhs)
	const { return Vector(x + rhs.x, y + rhs.y); }

	inline Vector operator * (const double&rhs)
	const { return Vector(x * rhs, y * rhs); }
	inline Vector operator / (const double&rhs)
	const { return Vector(x / rhs, y / rhs); }

	inline Vector operator * (const Vector&rhs)
	const { return Vector(x * rhs.x - y * rhs.y, x * rhs.y + y * rhs.x); }
}; typedef Vector Complex;

const double PI = std :: acos(-1.0);
const int Maxn = 6e5 + 5;

int lim = 1, rev[Maxn];
inline void FFT(int limit, Complex *arr, int type) {
	for (int i = 0; i < limit; i++)
		if (i < rev[i]) swap(arr[i], arr[rev[i]]);
	for (int mid = 1; mid < limit; mid <<= 1) {
		Complex w0(cos(PI / mid), type * sin(PI / mid));
		for (int i = 0; i < limit; i += mid << 1) { Complex w(1, 0);
			for (int j = 0; j < mid; j++, w = w * w0) {
				Complex x = arr[i + j], y = w * arr[i + j + mid];
				arr[i + j] = x + y, arr[i + j + mid] = x - y;
			}
		}
	} if (type == -1) for (int i = 0; i < limit; i++) arr[i] = arr[i] / limit;
}

int n, m, x; Complex f[Maxn];

signed main(void) {
	read(n), read(x);
	for (int i = 1, v; i <= n; i++) {
		read(v); m += v < x ? 1 : 0; f[m].x++;
	}
	
	f[0].x++;
	while (lim <= m << 1) lim <<= 1;
	for (int i = 0; i < lim; i++)
		rev[i] = (rev[i >> 1] >> 1) | ((i & 1) ? (lim >> 1) : 0);
	
	ll ret = 0;
	for (int i = 0; i <= m; i++) f[m - i].y = f[i].x, ret += (ll) f[i].x * (f[i].x - 1) / 2;
	
	FFT(lim, f, 1);
	for (int i = 0; i < lim; i++) f[i] = f[i] * f[i];
	FFT(lim, f, -1);
	
	writeln(ret, ' ');
	for (int i = 1; i <= m; i++) writeln((ll) (f[m - i].y / 2.0 + 0.5), ' ');
	for (int i = m + 1; i <= n; i++) writeln(0, " \n"[i == n]);
//	fwrite(pf, 1, o1 - pf, stdout);
	return 0;
}

相關文章