UOJ Contest #50: Goodbye Jihai


比賽傳送門:Goodbye Jihai

\(\Huge{\mathbf{再見,己亥。\\你好,庚子!\\祝大家新春快樂!}}\)


A. 新年的促銷

這題如果直接做的話可能方向會想歪,方向對了其實就是入門題。


考慮這個性質:假設花錢購買了 \(i\) 袋大米,最后獲得了 \(k\) 袋大米,則必然是花錢買了其中最便宜的 \(i\) 袋。

所以按照 \(p_i\) 從小到大排序,必然存在一個分界點,左側獲得的大米必然是花錢購買的,右側獲得的大米必然是贈送的。

做背包即可,要記一下購買了多少袋,后面通過預處理求出能夠通過贈送若干袋獲得的最大重量。

下面是代碼,時間復雜度為 \(\mathcal O (n^2 m)\)

#include <cstdio>
#include <cstring>
#include <algorithm>

const int MN = 305, MM = 1005;

int N, M, A, B;
int w[MN], v[MN], p[MN], t[MN];
int sw[MN][MN * 2];
int f[MN][MM];
int Ans[MM];

int main() {
	scanf("%d%d%d%d", &N, &M, &A, &B);
	for (int i = 1; i <= N; ++i) scanf("%d", &w[i]);
	for (int i = 1; i <= N; ++i) scanf("%d", &v[i]);
	for (int i = 1; i <= N; ++i) p[i] = i;
	std::sort(p + 1, p + N + 1, [](int i, int j) { return v[i] < v[j]; });
	for (int i = 1; i <= N; ++i) t[i] = w[p[i]];
	std::copy(t + 1, t + N + 1, w + 1);
	for (int i = 1; i <= N; ++i) t[i] = v[p[i]];
	std::copy(t + 1, t + N + 1, v + 1);
	for (int i = N; i >= 1; --i) {
		std::copy(w + i, w + N + 1, sw[i] + 1);
		std::sort(sw[i] + 1, sw[i] + N - i + 2, std::greater<int>());
		std::partial_sum(sw[i] + 1, sw[i] + N + N + 1, sw[i] + 1);
	}
	memset(f, 0xc0, sizeof f);
	f[0][0] = 0;
	for (int i = 1; i <= N; ++i) {
		for (int k = M - v[i]; k >= 0; --k)
			for (int j = 0; j < i; ++j)
				f[j + 1][k + v[i]] = std::max(f[j + 1][k + v[i]], f[j][k] + w[i]);
		for (int k = 0; k <= M; ++k)
			for (int j = 0; j <= i; ++j)
				Ans[k] = std::max(Ans[k], f[j][k] + sw[i + 1][j / A + j / B]);
	}
	std::partial_sum(Ans + 1, Ans + M + 1, Ans + 1, [](const int &a, const int &b) { return a > b ? a : b; });
	for (int i = 1; i <= M; ++i) printf("%d%c", Ans[i], " \n"[i == M]);
	return 0;
}

B. 新年的新航線

咕。

C. 新年的復讀機

咕。

D. 新年的追逐戰

賽時想出了除了生成函數的部分,但是因為壞蛋出題人 EI 沒有給適合我的部分分,導致我獲得了暴力分。


不難發現題目中定義的圖乘積運算滿足交換律結合律

對於所有 \(n\) 個圖的總乘積 \(H = G_1 \times G_2 \times \cdots \times G_n = \langle V^*, E^* \rangle\),有:
\(V^* = \{ \langle a_1, a_2, \ldots, a_n \rangle \: | \: a_i \in V_i \}\),以及
兩點 \(\langle a_1, a_2, \ldots, a_n \rangle\)\(\langle b_1, b_2, \ldots, b_n \rangle\) 之間有連邊,當且僅當對於每個 \(1 \le i \le n\),都有 \(\langle a_i, b_i \rangle \in E_i\)

考慮 \(H\) 的連通塊性質。通過手玩一些簡單情況,可以總結歸納:
首先,在每一個原圖 \(G_i\) 上都放置一個棋子,第 \(i\) 個棋子在 \(G_i\) 的點 \(a_i\) 上,那么 \(H\) 中與 \(u = \langle a_1, a_2, \ldots, a_n \rangle\) 鄰接的一條邊就相當於每個圖上的棋子都走恰好一步。
可以得出,如果這之中某一個棋子在孤立點上,那么 \(u\) 就不存在鄰接邊。
對於其它的情況,顯然第 \(i\) 個棋子能夠移動的范圍就是 \(G_i\)\(a_i\) 所在的連通塊的所有點。
因為每個連通塊都不是孤立點,所以棋子可以無限移動。因為連通塊之間是獨立的,接下來假設 \(n = 2\),即只計算兩個連通塊的乘積:

  1. 如果兩個連通塊都是二分圖,則它們的乘積恰好是 \(2\) 個二分圖。
    證明:為這兩個連通塊中的點進行黑白染色,則如果兩個棋子所在的初始節點同色,則任意移動都是同色的,否則都是不同色的。而且不難證明,兩種情況中的點都分別連通。而因為棋子要回到初始點必然要走偶數步,所以也不存在奇環,是二分圖。
  2. 如果其中一個連通塊是二分圖,另一個連通塊不是二分圖,則它們的乘積恰好是 \(1\) 個二分圖。
    證明:為二分圖中的點進行黑白染色,因為非二分圖內含有奇環,對於在非二分圖上的棋子,可以在奇環中繞一圈,而在二分圖上的棋子所在點的顏色就會改變。同樣的,這些點也是互相連通的。同樣因為二分圖中的棋子要回到初始點必然要走偶數步,所以也不存在奇環,是二分圖。
  3. 如果兩個連通塊都不是二分圖,則它們的乘積恰好是 \(1\) 個非二分圖。
    證明:同樣的,其中一個棋子在奇環中繞一圈,另一個棋子一直在一條邊上往返,則可以讓另一個棋子移動到邊的另一端,所以點都是連通的。兩個棋子在奇環上繞兩環長乘積步,則就是走了奇數步回到初始位置,不是二分圖。

更一般地,我們總結兩張圖乘積的規律:
對於有 \(n\) 個點的圖 \(G_1\),假設其中有 \(a\) 個孤立點,\(b\) 個非孤立點二分圖連通塊,\(c\) 個非二分圖連通塊,我們把這些信息記做 \(\begin{bmatrix}n\\a\\b\\c\end{bmatrix}\)
假設第二張圖 \(G_2\) 的信息為 \(\begin{bmatrix}m\\d\\e\\f\end{bmatrix}\)。則可以如下總結兩張圖的乘積 \(H\) 的規律:
\(H\) 中的點數顯然為 \(nm\),孤立點數為 \(am + nd - ad\),非孤立點二分圖連通塊數為 \(2be + bf + ce\),非二分圖連通塊數為 \(cf\)
則有圖信息之間的乘積公式: \(\begin{bmatrix}n\\a\\b\\c\end{bmatrix}\cdot\begin{bmatrix}m\\d\\e\\f\end{bmatrix}=\begin{bmatrix}nm\\am+nd-ad\\2be+bf+ce\\cf\end{bmatrix}\)

不難發現信息乘積的單位元為 \(\begin{bmatrix}1\\0\\0\\1\end{bmatrix}\),只要計算出每個 \(k_i\) 的信息之和,再依次乘起來即可得到圖的總乘積 \(H\) 的信息。

接下來考慮計算點數為 \(k_i\) 的所有有標號無向圖的信息之和,也即需要計算這些圖中的孤立點總數非孤立點二分圖連通塊總數非二分圖連通塊總數。請看下列生成函數推導:

\[\begin{aligned} \hat{G}_{\text{arbitrary}} &= \sum_{i = 0}^{\infty} 2^{\binom{i}{2}} \frac{z^i}{i!} & : & [1, 1, 2, 8, 64, 1024, \ldots] \\ \hat{G}_{\text{connected}} &= \ln \hat{G}_{\text{arbitrary}} & : & [0, 1, 1, 4, 38, 728, \ldots] \\ \hat{C}_{\text{connected}} &= \hat{G}_{\text{connected}} \hat{G}_{\text{arbitrary}} & : & [0, 1, 3, 13, 98, 1398, \ldots]\\ \hat{C}_{\text{isolated}} &= z \, \hat{G}_{\text{arbitrary}} & : & [0, 1, 2, 6, 32, 320, \ldots] \\ \hat{G}_{\text{colored bipartite}} &= \sum_{i = 0}^{\infty} \sum_{j = 0}^{\infty} \binom{i + j}{i} 2^{ij} \frac{z^{(i + j)}}{(i + j)!} & & \\ &= \sum_{i = 0}^{\infty} \sum_{j = 0}^{\infty} \frac{2^{-\binom{i}{2}}}{i!} \frac{2^{-\binom{j}{2}}}{j!} 2^{\binom{i + j}{2}} z^{(i + j)} & & \\ &= \sum_{n = 0}^{\infty} 2^{\binom{n}{2}} z^n [z^n] {\left( \sum_{i = 0}^{\infty} 2^{-\binom{i}{2}} \frac{z^i}{i!} \right)}^2 & : & [1, 2, 6, 26, 162, 1442, \ldots] \\ \hat{G}_{\text{con-bipartite(not isolated)}} &= \frac{\ln \hat{G}_{\text{colored bipartite}}}{2} - z & : & [0, 0, 1, 3, 19, 195, \ldots] \\ \hat{C}_{\text{con-bipartite(not isolated)}} &= \hat{G}_{\text{con-bipartite(not isolated)}} \hat{G}_{\text{arbitrary}} & : & [0, 0, 1, 6, 43, 430, \ldots] \\ \hat{C}_{\text{connected, not bipartite}} &= \hat{C}_{\text{connected}} - \hat{C}_{\text{isolated}} - \hat{C}_{\text{con-bipartite(not isolated)}} & : & [0, 0, 0, 1, 23, 648, \ldots] \end{aligned} \]

其中:
\(\hat{G}_{\text{arbitrary}}\) 表示 \(n\) 個點的有標號無向圖總數的 \(\mathbf{EGF}\)
\(\hat{G}_{\text{connected}}\) 表示 \(n\) 個點的有標號連通無向圖總數的 \(\mathbf{EGF}\)
\(\hat{C}_{\text{connected}}\) 表示所有 \(n\) 個點的有標號無向圖中的連通塊數量總和的 \(\mathbf{EGF}\)
\(\hat{C}_{\text{isolated}}\) 表示所有 \(n\) 個點的有標號無向圖中的孤立點數總和的 \(\mathbf{EGF}\)
\(\hat{G}_{\text{colored bipartite}}\) 表示 \(n\) 個點的點二染色的有標號無向二分圖總數的 \(\mathbf{EGF}\),求它的技巧是利用 \(\displaystyle \binom{i + j}{2} = \binom{i}{2} + \binom{j}{2} + ij\) 分離枚舉變量 \(i, j\) 使其相互獨立;
\(\hat{G}_{\text{con-bipartite(not isolated)}}\) 表示 \(n\) 個點的非孤立點有標號連通無向二分圖總數的 \(\mathbf{EGF}\),因為連通二分圖恰好有兩種染色方案,所以將 \(\hat{G}_{\text{colored bipartite}}\) 取對數后再除以 \(2\) 就得到了連通二分圖的 \(\mathbf{EGF}\),再減去 \(z\) 是為了去除孤立點的情況;
\(\hat{C}_{\text{con-bipartite(not isolated)}}\) 表示所有 \(n\) 個點的有標號無向圖中的非孤立點二分圖連通塊數量總和的 \(\mathbf{EGF}\)
\(\hat{C}_{\text{connected, not bipartite}}\) 表示所有 \(n\) 個點的有標號無向圖中的非二分圖連通塊數量總和的 \(\mathbf{EGF}\)

按照生成函數計算即可。

下面是代碼,時間復雜度為 \(\mathcal O (n + \max k_i \log \max k_i)\)

#include <cstdio>
#include <algorithm>

typedef long long LL;
const int Mod = 998244353, Inv2 = (Mod + 1) / 2;
const int G = 3, iG = 332748118;
const int MS = 1 << 18;

inline int qPow(int b, int e) {
	int a = 1;
	for (; e; e >>= 1, b = (LL)b * b % Mod)
		if (e & 1) a = (LL)a * b % Mod;
	return a;
}

inline int gInv(int b) { return qPow(b, Mod - 2); }

int Inv[MS], Fac[MS], iFac[MS];

inline void Init(int N) {
	Fac[0] = 1;
	for (int i = 1; i < N; ++i) Fac[i] = (LL)Fac[i - 1] * i % Mod;
	iFac[N - 1] = gInv(Fac[N - 1]);
	for (int i = N - 1; i >= 1; --i) iFac[i - 1] = (LL)iFac[i] * i % Mod;
	for (int i = 1; i < N; ++i) Inv[i] = (LL)Fac[i - 1] * iFac[i] % Mod;
}

inline int Binom(int N, int M) {
	if (M < 0 || M > N) return 0;
	return (LL)Fac[N] * iFac[M] % Mod * iFac[N - M] % Mod;
}

int Sz, InvSz, R[MS];

inline int getB(int N) { int Bt = 0; while (1 << Bt < N) ++Bt; return Bt; }

inline void InitFNTT(int N) {
	int Bt = getB(N);
	if (Sz == (1 << Bt)) return ;
	Sz = 1 << Bt, InvSz = Mod - (Mod - 1) / Sz;
	for (int i = 1; i < Sz; ++i) R[i] = R[i >> 1] >> 1 | (i & 1) << (Bt - 1);
}

inline void FNTT(int *A, int Ty) {
	for (int i = 0; i < Sz; ++i) if (R[i] < i) std::swap(A[R[i]], A[i]);
	for (int j = 1, j2 = 2; j < Sz; j <<= 1, j2 <<= 1) {
		int wn = qPow(~Ty ? G : iG, (Mod - 1) / j2), w, X, Y;
		for (int i = 0, k; i < Sz; i += j2) {
			for (k = 0, w = 1; k < j; ++k, w = (LL)w * wn % Mod) {
				X = A[i + k], Y = (LL)w * A[i + j + k] % Mod;
				A[i + k] -= (A[i + k] = X + Y) >= Mod ? Mod : 0;
				A[i + j + k] += (A[i + j + k] = X - Y) < 0 ? Mod : 0;
			}
		}
	}
	if (!~Ty) for (int i = 0; i < Sz; ++i) A[i] = (LL)A[i] * InvSz % Mod;
}

inline void PolyConv(int *_A, int N, int *_B, int M, int *_C, int tN = -1) {
	if (tN == -1) tN = N + M - 1;
	static int A[MS], B[MS];
	InitFNTT(N + M - 1);
	for (int i = 0; i < N; ++i) A[i] = _A[i];
	for (int i = N; i < Sz; ++i) A[i] = 0;
	for (int i = 0; i < M; ++i) B[i] = _B[i];
	for (int i = M; i < Sz; ++i) B[i] = 0;
	FNTT(A, 1), FNTT(B, 1);
	for (int i = 0; i < Sz; ++i) A[i] = (LL)A[i] * B[i] % Mod;
	FNTT(A, -1);
	for (int i = 0; i < tN; ++i) _C[i] = A[i];
}

inline void PolyInv(int *_A, int N, int *_B) {
	static int A[MS], B[MS], tA[MS], tB[MS];
	for (int i = 0; i < N; ++i) A[i] = _A[i];
	for (int i = N, Bt = getB(N); i < 1 << Bt; ++i) A[i] = 0;
	B[0] = gInv(A[0]);
	for (int L = 1; L < N; L <<= 1) {
		int L2 = L << 1, L4 = L << 2;
		InitFNTT(L4);
		for (int i = 0; i < L2; ++i) tA[i] = A[i];
		for (int i = L2; i < Sz; ++i) tA[i] = 0;
		for (int i = 0; i < L; ++i) tB[i] = B[i];
		for (int i = L; i < Sz; ++i) tB[i] = 0;
		FNTT(tA, 1), FNTT(tB, 1);
		for (int i = 0; i < Sz; ++i) tB[i] = tB[i] * (2 - (LL)tA[i] * tB[i] % Mod + Mod) % Mod;
		FNTT(tB, -1);
		for (int i = 0; i < L2; ++i) B[i] = tB[i];
	}
	for (int i = 0; i < N; ++i) _B[i] = B[i];
}

inline void PolyLn(int *_A, int N, int *_B) {
	static int tA[MS], tB[MS];
	for (int i = 1; i < N; ++i) tA[i - 1] = (LL)_A[i] * i % Mod;
	PolyInv(_A, N - 1, tB);
	InitFNTT(N + N - 3);
	for (int i = N - 1; i < Sz; ++i) tA[i] = 0;
	for (int i = N - 1; i < Sz; ++i) tB[i] = 0;
	FNTT(tA, 1), FNTT(tB, 1);
	for (int i = 0; i < Sz; ++i) tA[i] = (LL)tA[i] * tB[i] % Mod;
	FNTT(tA, -1);
	_B[0] = 0;
	for (int i = 1; i < N; ++i) _B[i] = (LL)tA[i - 1] * Inv[i] % Mod;
}

const int MN = 100005;

inline void Merge(int *A, int *B) {
	static int C[4];
	C[0] = (LL)A[0] * B[0] % Mod;
	C[1] = ((LL)A[1] * B[0] + (LL)A[0] * B[1] - (LL)A[1] * B[1]) % Mod;
	if (C[1] < 0) C[1] += Mod;
	C[2] = (2ll * A[2] * B[2] + (LL)A[2] * B[3] + (LL)A[3] * B[2]) % Mod;
	C[3] = (LL)A[3] * B[3] % Mod;
	A[0] = C[0], A[1] = C[1], A[2] = C[2], A[3] = C[3];
}

int N, K[MN], MaxK;
int A1[MS], A2[MS], A3[MS], A4[MS], A5[MS], A6[MS], A7[MS], A8[MS];
int C0[MS], C1[MS], C2[MS], C3[MS];

int main() {
	scanf("%d", &N);
	for (int i = 1; i <= N; ++i) {
		scanf("%d", &K[i]);
		MaxK = std::max(MaxK, K[i]);
	}
	Init(MaxK + 1);
	for (int i = 0; i <= MaxK; ++i) A1[i] = (LL)qPow(2, (LL)i * (i - 1) / 2 % (Mod - 1)) * iFac[i] % Mod;
	PolyLn(A1, MaxK + 1, A2);
	PolyConv(A2, MaxK + 1, A1, MaxK + 1, A3, MaxK + 1);
	for (int i = 1; i <= MaxK; ++i) A4[i] = A1[i - 1];
	for (int i = 0; i <= MaxK; ++i) A5[i] = (LL)qPow(Inv2, (LL)i * (i - 1) / 2 % (Mod - 1)) * iFac[i] % Mod;
	PolyConv(A5, MaxK + 1, A5, MaxK + 1, A5, MaxK + 1);
	for (int i = 0; i <= MaxK; ++i) A5[i] = (LL)A5[i] * qPow(2, (LL)i * (i - 1) / 2 % (Mod - 1)) % Mod;
	PolyLn(A5, MaxK + 1, A6);
	for (int i = 0; i <= MaxK; ++i) A6[i] = (LL)A6[i] * Inv2 % Mod;
	--A6[1];
	PolyConv(A6, MaxK + 1, A1, MaxK + 1, A7, MaxK + 1);
	for (int i = 0; i <= MaxK; ++i) A8[i] = ((LL)A3[i] - A4[i] - A7[i] + Mod * 2) % Mod;
	for (int i = 0; i <= MaxK; ++i) {
		C0[i] = (LL)i * qPow(2, (LL)i * (i - 1) / 2 % (Mod - 1)) % Mod;
		C1[i] = (LL)A4[i] * Fac[i] % Mod;
		C2[i] = (LL)A7[i] * Fac[i] % Mod;
		C3[i] = (LL)A8[i] * Fac[i] % Mod;
	}
	int Ans[4] = {1, 0, 0, 1};
	for (int i = 1; i <= N; ++i) {
		static int Now[4];
		Now[0] = C0[K[i]], Now[1] = C1[K[i]],
		Now[2] = C2[K[i]], Now[3] = C3[K[i]];
		Merge(Ans, Now);
	}
	printf("%lld\n", ((LL)Ans[1] + Ans[2] + Ans[3]) % Mod);
	return 0;
}

E. 新年的邀請函

咕。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM