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


本文來自博客園,作者:xrlong,轉載請注明原文鏈接:http://www.rzrgm.cn/xrlong/p/18700110
版權聲明:本作品采用 「署名-非商業性使用-相同方式共享 4.0 國際」許可協議(CC BY-NC-SA 4.0) 進行許可。

浙公網安備 33010602011771號