Luogu P9542 [湖北省選模擬 2023] 棋聖 alphago

rizynvu發表於2024-07-01

2023.08.19:修改了一處筆誤。

手玩發現對於一顆生成樹,如果存在至少一個點的度數 \(> 2\)(即不為鏈),那麼肯定能使得所有棋子都在一條邊的兩個端點上。
因為有度數 \(> 2\) 的點的存在,這裡就可以合併與其相連的點的棋子。

先考慮非鏈的情況的答案,記兩部分棋子黑白棋子顏色分別為 \(c(a/b)(0/1)\)
由兩部分棋子在一條邊上考慮到二分圖。
若為二分圖,則不管怎麼走兩部分棋子所在的顏色都是不同的,即兩部分棋子最優情況也還是中間有一條邊,答案為 \(w_{\max}\times (c(a)(0)\times c(b)(1) + c(a)(1)\times c(b)(0))\)
若不為二分圖,則圖上肯定存在奇環,可以利用這個奇環分割開黑白棋子並把黑白棋子分別湊在一起,答案為 \(w_{\max}\times ((c(a)(0) + c(b)(0))\times (c(a)(1) + c(b)(1)))\)

再來考慮鏈的情況。
手玩能發現最後棋子的位置需要滿足 \(3\) 點:

  1. 在鏈上相鄰的兩個點操作完的距離 \(\le\) 兩個點的初始距離,因為發現選取的空節點只存在兩點之間或之外的情況,而距離變化分別為 \(-2\) 和不變。
  2. 在鏈上相鄰的兩個點不會在操作中調換順序,證明與 1 相似。
  3. 在鏈上相鄰的兩個點操作完的距離與兩個點的初始距離模 \(2\) 意義下同餘,因為鏈也是個二分圖。

考慮 DP,設 \(f_{i, l, r}\) 為到鏈上第 \(i\) 個點且這個點上有編號為 \([l, r]\) 的棋子的最大權值,不合法的值為 \(-\inf\)
\(\operatorname{count}([l, r], [L, R])\) 為這 \(2\) 個區間的棋子能產生的配對數,\(\operatorname{dis}(x, y)\) 為這兩個棋子在鏈上的距離。
轉移分為 \(2\) 部分:

  • \(i - 1\) 選的點與 \([l, r]\) 產生貢獻,\(f_{i, l, r} = \max\limits_{L = 1}^{l - 1} ([\operatorname{dis}(l - 1, l)\equiv 1\pmod 2]\times (f_{i - 1, L, l - 1} + \operatorname{count}([L, l - 1], [l, r])\times w(i - 1, i)))\),可以在列舉的時候維護 \(\operatorname{count}\),單次 \(O(n)\)
  • 從前面的點轉移過來,但是不產生貢獻,\(f_{i, l, r} = \max\limits_{j = i - \operatorname{dis(l, l - 1)}}^{i - 1}([i - j\equiv \operatorname{dis}(l - 1, l)\pmod 2]\times (\max\limits_{L = 1}^{l - 1} f_{j, L, l - 1}))\),但這樣是 \(O(n^2)\) 的。
    發現對於每個 \(j\) 求的就是右端點為 \(l - 1\) 的最大值,可以預處理 \(g_{i, r} = \max\limits_{l = 1}^r f_{i, l, r}\),轉移式便為 \(f_{i, l, r} = \max\limits_{j = i - \operatorname{dis(l, l - 1)}}^{i - 1}([i - j\equiv \operatorname{dis}(l - 1, l)\pmod 2]\times g_{j, l - 1})\),時間複雜度 \(O(n)\)

時間複雜度 \(\mathcal{O}(n^4)\)

#include<bits/stdc++.h>
const int N = 1e2 + 10;
int n, m, k;
std::vector<int> G[N];
int cx[N], cy[N];
int Gx[N * N], Gy[N * N], Gz[N * N];
int deg[N], deg_cnt[N];
int check_Case1() {
	for (int i = 1; i <= m; i++) {
		deg[Gx[i]]++, deg[Gy[i]]++;
	}
	for (int i = 1; i <= n; i++) {
		deg_cnt[deg[i]]++;
	}
	return deg_cnt[1] == 2 && deg_cnt[2] == n - 2;
}
int mcol[N];
void dfs(int u, int &flg) {
	for (auto v : G[u]) {
		if (mcol[v] == -1) {
			mcol[v] = mcol[u] ^ 1, dfs(v, flg);
		} else {
			flg &= mcol[u] != mcol[v];
		}
	}
}
int check_Case2() {
	for (int i = 1; i <= m; i++) {
		G[Gx[i]].push_back(Gy[i]), G[Gy[i]].push_back(Gx[i]);
	}
	memset(mcol, -1, sizeof(int) * (n + 1));
	int flg = 1;
	mcol[1] = 0, dfs(1, flg);
	return flg;
}
int cnt[2][2];
std::pair<int, int> c[N];
std::vector<std::pair<int, int> > Gc[N];
int dep[N], w[N];
int f[N][N][N], g[N][N];
int d[N], rd[N], ld[N];
int main() {
	scanf("%d%d%d", &n, &m, &k);
	for (int i = 1; i <= k; i++) {
		scanf("%d%d", &cx[i], &cy[i]);
	}
	for (int i = 1; i <= m; i++) {
		scanf("%d%d%d", &Gx[i], &Gy[i], &Gz[i]);
	}
	if (check_Case1()) {
		for (int i = 1; i <= m; i++) {
			Gc[Gx[i]].emplace_back(Gy[i], Gz[i]), Gc[Gy[i]].emplace_back(Gx[i], Gz[i]);
		}	
		int st = -1, ed = -1;
		for (int i = 1; i <= n; deg[i] == 1 && (st == -1 ? st = i : ed = i, 1), i++);
		for (int i = st, d = 1; d <= n; d++) {
			dep[i] = d;
			std::pair<int, int> v = dep[Gc[i][0].first] == 0 ? Gc[i][0] : Gc[i][1];
			i = v.first, w[d + 1] = v.second;
		}
		for (int i = 1; i <= k; i++) {
			c[i] = {cx[i], cy[i]};
		}
		std::sort(c + 1, c + k + 1, [](std::pair<int, int> a, std::pair<int, int> b) {
			return dep[a.first] < dep[b.first];
		});
		for (int i = 1; i < k; i++) {
			d[i] = dep[c[i + 1].first] - dep[c[i].first];
		}
		rd[k] = k;
		for (int i = k - 1; i; i--) {
			rd[i] = (d[i] & 1) ? i : rd[i + 1]; 
		}
		ld[1] = 1;
		for (int i = 2; i <= k; i++) {
			ld[i] = (d[i - 1] & 1) ? i : ld[i - 1];
		}
		memset(f, -0x3f, sizeof(f));
		memset(g, -0x3f, sizeof(g));
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= rd[1]; j++) {
				f[i][1][j] = 0, g[i][j] = 0;
			}
		}
		for (int i = 2; i <= n; i++) {
			for (int l = 2; l <= k; l++) {
				cnt[0][0] = cnt[1][0] = 0;
				int R = l - 1;
				for (int r = l; r <= rd[l]; r++) {
					cnt[c[r].second][0]++;
					if (d[R] & 1) {
						cnt[0][1] = cnt[1][1] = 0;
						for (int L = R; L >= ld[R]; L--) {
							cnt[c[L].second][1]++;
							f[i][l][r] = std::max(f[i][l][r], f[i - 1][L][R] + w[i] * (cnt[0][0] * cnt[1][1] + cnt[1][0] * cnt[0][1]));
						}
					}
					for (int j = i - d[R] > 0 ? i - d[R] : (((i - d[R]) & 1) ? 1 : 2); j < i; j += 2) {
						f[i][l][r] = std::max(f[i][l][r], g[j][R]);
					}
					g[i][r] = std::max(g[i][r], f[i][l][r]);
				}
			}
		}
		int ans = f[0][0][0];
		for (int i = 1; i <= n; i++) {
			for (int l = ld[k]; l <= k; l++) {
				ans = std::max(ans, f[i][l][k]);
			}
		}
		printf("%d\n", ans);
	} else if (check_Case2()) {
		for (int i = 1; i <= k; i++) {
			cnt[cy[i]][mcol[cx[i]]]++;
		}
		int mxz = 0;
		for (int i = 1; i <= m; mxz = std::max(mxz, Gz[i]), i++);
		printf("%d\n", mxz * (cnt[0][0] * cnt[1][1] + cnt[0][1] * cnt[1][0]));
	} else {
		for (int i = 1; i <= k; i++) {
			cnt[cy[i]][0]++;
		}
		int mxz = 0;
		for (int i = 1; i <= m; mxz = std::max(mxz, Gz[i]), i++);
		printf("%d\n", mxz * (cnt[0][0] * cnt[1][0]));
	}
	return 0;
}

相關文章