2025.2.5 鲜花

如何优雅的用反悔贪心思路做出模拟费用流(HZTG3003. 皮胚 题解)

みむかゥわナイストライ
UN◯♡
おやぁ?
手札多過ぎクッソワロタw
次のターンで私が勝ちまちゅね♡
そのザコさの秘訣を教えてちょ♡
う・じ・む・し・ちゃ・ん♡
ねェカッコわるく負けちゃァだぁ〜めっ
ほら が〜んばれっ♡が〜んばれっ♡
ホンキ出したら勝てるんでしょ(笑)
は・や・く・み・せ・て?♡(煽り)
(十枚)︙ズ干Cャ 丄丶"㐴団" /\
吖︙㾎ヅKャ 1了‹‹ 10 ‹ノ1/\
此奴はイわずと知れた
い・く・じ・な・し♡
ジャケラ Ha Ha Ha
ぴえん 越えて だっさぁ♡
Ow Ow Ow ぱおん 越えて ざっまぁ♡
Beh Beh Beh You're an idiot ☻
されど その場しのぎは→ Shhh
ジャケラ Ha Ha Ha
ぴえん 越えて だっさぁ♡
Ow Ow Ow ぱおん 越えて ざっまぁ♡
Beh Beh Beh GAME SET ハイ
みむかゥわナイストライ♡
ざぁーこ♡ざぁーこ♡たぁ♡
ざぁーこ♡ざぁーこ♡わぁ♡
ざぁーこ♡ざぁーこ♡けぇ♡
酸性雨に埋まっちゃえ♡Cla-Cla-Clap
ざぁーこ♡ざぁーこ♡ばぁー♡
ざぁーこ♡ざぁーこ♡か♡
ざぁーこ♡ざぁーこ♡
=Dynamite= 33-4
なんで手札出さねえの?
ひょっとして ひよってんの?
まじくっそだせえな
あ・く・し・ろ・よ(・д・)チッ
ざこざこざこざこざこざこざこざこ
ざこざこざこざこざこざこざこざこ
ざこざこざこざこざこざこざこざこ
ね 早く出そ?
もし私に勝ったら
此奴の言うことを
なんでも聞いたげるよ♡
まあ 勝てないけどっ♡(笑)
噛ませ犬に抜擢
よわよわ此奴ァわ♡
所詮先の時代の
は・い・ぼ・く・しゃ ♡
ジャケェラ Ha Ha Ha
ぴえん 越えて だっさぁ♡
Ow Ow Ow ぱおん 越えて ざっまぁ♡
Beh Beh Beh You're an idiot ☻
されど その場しのぎ は→ Shhh
ジャケラ Ha Ha Ha
ぴえん 越えて だっさぁ♡
Ow Ow Ow ぱおん 越えて ざっまぁ♡
Beh Beh Beh GAME SET ハイ
みむかゥわナイストライ♡
ざぁーこ♡ざぁーこ♡たぁ♡
ざぁーこ♡ざぁーこ♡わぁ♡
ざぁーこ♡ざぁーこ♡けぇ♡
酸性雨に埋まっちゃえ♡Cla-Cla-Clap
ざぁーこ♡ざぁーこ♡ばぁー♡
ざぁーこ♡ざぁーこ♡かぁ♡
ざぁーこ♡ざぁーこ♡
たわいない最終回 対ヨロ
ざぁーこ♡ざぁーこ♡
ざぁーこ♡ざぁーこ♡
ざぁーこ♡ざぁーこ♡
=Dynamite= 33-4
負けちゃお?♡負けちゃお?♡ね?
みじめに生き恥さらしちゃお?♡
出せっ♡出せっ♡出せっ♡出せっ♡
"敗北宣言" ぶちまけろっっ♡
う"わ"あ"あ"あ"あ"あ"あ"
あ"あ"あ"あ"ん"ん"(豆腐メンタル)

我不会费用流。

考虑贪心做这道题,首先显然的贪心是每次选最大的没匹配的边,也显然假。

考虑假的原因,发现其事实上可以通过交错路来匹配两个不相关的点,考虑将这样的点对之间连边当做是反悔策略,贡献就是和原来的差。

考虑到每个节点的度最多是 \(20\),正确性和复杂度都是有保证的,但是因为交错路是隔一条边选一条边,很难真正的维护出交错路形成的连通块的形状和叶子(度是一的点),也就很难做到直接将所有决策统计起来。

发现关键性质,\(k \le 200\),这给了我们暴力的机会,考虑每次枚举一条边,我们并不维护所有决策,我们只维护出从某个点出发跳交错路的最大贡献,这个暴力 dfs 跳交错路是容易的。

因为每次最多多选 \(1\) 条边,所以连通块大小也是 \(\mathcal{O}(k)\) 的,单次的复杂度就是 \(k ^ 2\),实现的时候可以先建出整个的图,将边黑白染色,然后暴力交替跳黑白色即可。

现在的复杂度是 \(2 ^ n n k ^ 2\),发现边数很多,但是只有 \(\mathcal{O}(n k)\) 条和交错路连通块相邻的边需要跑交错路,于是这部分我们暴力枚举交错路连通块里的点和与其相连的边来统计,其他边就依然放到堆里跑贪心。

注意到我们的交错路只和与交错路连通块相交的那个点有关,所以枚举边并不在复杂度上限。

发现这些放到堆里的边权值都不变,于是直接基排后用队列维护就可以了,复杂度 \(\mathcal{O}(2 ^ n n + k ^ 3 n)\),足以通过。

跳交错路相当于是退流,所以写出来和模拟费用流一模一样啦~

Code
#include <bits/stdc++.h>
using namespace std;
using llt = long long;
using ull = unsigned long long;
using llf = long double;
#define endl '\n'
#ifdef LOCAL
FILE *InFile = freopen("in_out/in.in", "r", stdin), *OutFile = freopen("in_out/out.out", "w", stdout);
#else
FILE *InFile = freopen("pp.in", "r", stdin), *OutFile = freopen("pp.out", "w", stdout);
#endif

const int LG = 20, N = (1 << LG) + 3;
int n, ck, clg, cv[N];
pair<int, int> ce[N * 10], *ft = ce + 1, *bk = ce + 1;

struct Gph{
	int hd[N], nt[N * 20], to[N * 20], tot = 1; bool wt[N * 20];
	void Add(int u, int v, bool w){
		wt[++tot] = w, to[tot] = v, nt[tot] = hd[u], hd[u] = tot;
	}
	void ADD(int u, int v, int w){
		Add(u, v, w), Add(v, u, w);
	}
#define For_to(i, u, v, g) for(int i = g.hd[u], v = g.to[i]; i; i = g.nt[i], v = g.to[i]) 
} g;

vector<int> nd;
bool vnd[N]; int clv, to[N], vis[N];
int aaa;
int Dfs(int u, bool l){
	++aaa;
	vis[u] = clv;
	if(!vnd[u]) return cv[u];
	int ma = -0x3f3f3f3f;
	For_to(i, u, v, g) if(vis[v] != clv && g.wt[i] != l)
		ma = max(ma, Dfs(v, l ^ 1));
	return ma;
}
int To(int u, bool l){
	vis[u] = clv;
	if(!vnd[u]) return cv[u];
	int ma = -0x3f3f3f3f;
	For_to(i, u, v, g) if(vis[v] != clv && g.wt[i] != l){
		int t = To(v, l ^ 1);
		if(ma < t)
			ma = t, to[u] = v;
	}
	return ma;
}
void Rev(int u, bool l){
	if(!vnd[u]){
		nd.emplace_back(u), vnd[u] = 1;
		return ;
	}
	For_to(i, u, v, g) if(v == to[u])
		Rev(v, l ^ 1), g.wt[i] ^= 1, g.wt[i ^ 1] ^= 1;
};

int main(){
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	cin >> clg >> ck; n = 1 << clg;
	for(int i = 0; i < n; ++i)
		cin >> cv[i];
	{
		static pair<int, int> tp[N * 10]; int ct = 0;
		for(int u = 0; u < n; ++u)
			for(int j = 0; j < clg; ++j) if(u & (1 << j)){
				int v = u ^ (1 << j);
				g.ADD(u, v, 0);
				tp[++ct] = {u, v};
			}
		const int V = 2e6 + 3;
		static int tmp[V];
		for(int i = 1; i <= ct; ++i){
			auto &[u, v] = tp[i];
			++tmp[cv[u] + cv[v]];
		}
		for(int i = V - 3; ~i; --i)
			tmp[i] += tmp[i + 1];
		for(int i = ct; i; --i){
			auto &[u, v] = tp[i];
			ce[tmp[cv[u] + cv[v]]--] = tp[i];
		}
		bk += ct;
	}
	int ans = 0;
	memset(to, -1, sizeof to);
	for(int ccc = 1; ccc <= ck; ++ccc){
		while(ft != bk){
			auto [u, v] = *ft; 
			if(vnd[u] || vnd[v]) ++ft;
			else break;
		}
		auto [eu, ev] = *ft;
		int ema = -1;
		if(ft != bk)
			ema = cv[eu] + cv[ev];
		int nma = 0, nu = 0, nv = 0;
		for(auto u : nd){
			int tp = Dfs(u, 0);
			For_to(i, u, v, g) if(!g.wt[i]){
				int t = tp + cv[v] * (!vnd[v]);
				if(t > nma)
					nma = t, nu = u, nv = v;
				++clv;
			}
		}
		if(max(nma, ema) <= 0) break;
		if(nma > ema){
			ans += nma;
			To(nu, 0), ++clv;
			Rev(nu, 0), ++clv;
			For_to(i, nu, v, g) if(v == nv)
				g.wt[i] = g.wt[i ^ 1] = 1;
			if(!vnd[nu])
				nd.emplace_back(nu), vnd[nu] = 1;
			if(!vnd[nv])
				nd.emplace_back(nv), vnd[nv] = 1;
		}else{
			ans += ema;
			++ft;
			For_to(i, eu, v, g) if(v == ev)
				g.wt[i] = g.wt[i ^ 1] = 1;
			nd.emplace_back(eu), nd.emplace_back(ev), vnd[eu] = vnd[ev] = 1;
		}
	}
	cout << ans << endl;
}
P

posted @ 2025-02-05 20:38  5k_sync_closer  阅读(76)  评论(8编辑  收藏  举报