闲话 22.12.13

闲话

今天比较想摆
计数 \(n\) 个点的有标号无向图,满足每个点的度数是 \(2\)
咋写啊?

有社论推荐吗?

看不懂计数题解咋办?

这首歌挺好听的!
就是副歌容易让人想到电摇(((

续写续写

大概都知道我要写啥了吧?放个插件

植物大战僵尸

当时讲的时候觉得没啥应用 转头打脸了

就是求最大权闭合子图。我们把关系抽象成图,”吃掉 A 才能吃 B“就连一条 \(B\to A\) 的边,表示选了 \(B\) 必须要选 \(A\)。然后我们需要从这个图里选出一个闭合的子图(没有向子图外连的边)。
咋求呢?我们首先建出原图,边权全是正无穷,再整两个超级点。源点连所有权为整数的值,边权为权,汇点连所有权为负数的值,边权为负权。然后最大权就是正权加和减去最小割。
咋证的不会。我们仍然沿用 happiness 模型,这里割了连源的边就是不选,割了连汇的边就是选。然后你肯定不能割原图的边,则有一条 \(S\to u \to v\to T\) 的路径的话肯定要选 \(v\) 或不选 \(u\)。正确性显然了。

注意先做一遍拓扑排序,删掉成环的植物和它们指向的植物。

code
const int N = 2e6 + 5;
const int inf = 0x3f3f3f3f;
const ll infll = 0x3f3f3f3f3f3f3f3fll;
int M, s, t, n, m, t1, t2, ans, sc[1005], at[1005], deg[1005];
vi g[1005]; bool vis[1005];
#define id(i, j) ( ((i) - 1) * m + (j) )

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n >> m;
    M = n * m; s = ++ M, t = ++ M;
    rep(i,1,n) rep(j,1,m) {
        if(j < m) {
            g[id(i, j + 1)].eb(id(i, j));
            // adde(id(i, j), id(i, j + 1), inf);
            deg[id(i, j)] ++;
        }
        cin >> sc[id(i, j)] >> at[id(i, j)];
        rep(k,1,at[id(i, j)]) {
            cin >> t1 >> t2; ++ t1, ++ t2;
            g[id(i, j)].eb(id(t1, t2));
            // adde(id(t1, t2), id(i, j), inf);
            deg[id(t1, t2)] ++;
        }
    }
    queue<int> que;
    rep(i,1,n) rep(j,1,m) if (deg[id(i, j)] == 0) que.emplace(id(i, j)), vis[id(i, j)] = 1;
    while (que.size()) {
        int u = que.front(); que.pop();
        for (auto v : g[u]) {
            -- deg[v];
            if (!vis[v] and deg[v] == 0) vis[v] = 1, que.emplace(v);
        }
    }
    rep(i,1,n) rep(j,1,m) if (vis[id(i, j)]) {
        if (sc[id(i, j)] > 0) adde(s, id(i, j), sc[id(i, j)]), ans += sc[id(i, j)];
        else adde(id(i, j), t, - sc[id(i, j)]);
        for (auto v : g[id(i, j)]) if (vis[v]) adde(v, id(i, j), inf);
    }
    cout << ans - Dinic(s, t) << '\n';
}



清理雪道

最小……流?

没错,有源汇上下界最小流。
咋成图呢?假设你原来要连一条 \((u,v), [l,r]\) 的边,那就连一条 \((u, v, r - l)\) 的边,然后开一个数组 \(d\)\(d_u\) 减去 \(l\)\(d_v\) 加上 \(l\)。成原图后在原图的源汇外建超级源汇,如果 \(d_i > 0\) 就和源连,反之和汇连,容量就是 \(|d_i|\)。然后以超级源汇为源汇跑一遍最大流。再加一条原图汇到源的 \([0,\inf]\) 边,在残量网络上跑超级源汇的最大流,这条边的流量就是最小流。
好奇妙啊!感性理解即可吧!发现这个就是在可行流的基础上在加转移边前跑了一遍最大流,这样能将转移的流/不满足流量守恒的流量尽可能减少,也就是尽可能减少了源到汇的浮动流量。浮动流量的最小值不就是最大流吗?

code
const int N = 2e6 + 5;
const int inf = 0x3f3f3f3f;
const ll infll = 0x3f3f3f3f3f3f3f3fll;
int M, s, t, S, T, n, m, ans, t1, t2, t3, t4;

int head[2][N], mlc = 1;
struct ep {
    int to, next, flow;
} e[N << 1];
void __Adde(int u, int v, int f) {
    e[++ mlc] = { v, head[0][u], f };
    head[0][u] = mlc;
    e[++ mlc] = { u, head[0][v], 0 };
    head[0][v] = mlc; 
}

int d[N];
void adde(int u, int v, int Min, int Max) {
    __Adde(u, v, Max - Min);
    d[u] -= Min, d[v] += Min;
}

int dep[N]; 
bool bfs(int s, int t) {
    rep(i,1,M) dep[i] = 0, head[1][i] = head[0][i];
    queue<int> que;
    que.push(s); dep[s] = 1;
    while (que.size()) {
        int u = que.front(); que.pop();
        for (int i = head[0][u], v; i; i = e[i].next) {
            v = e[i].to;
            if (e[i].flow and !dep[v]) {
                dep[v] = dep[u] + 1;
                if (v == t) return 1;
                que.push(v);
            }
        }
    } return dep[t];
}

int dfs(int u, int in, int t) {
    if (u == t) return in;
    int out = 0;
    for (int &i = head[1][u], v; i; i = e[i].next) {
        v = e[i].to;
        if (e[i].flow and dep[v] == dep[u] + 1) {
            int res = dfs(v, min(in, e[i].flow), t);
            if (res == 0) dep[v] = 0;
            in -= res, out += res;
            e[i].flow -= res, e[i ^ 1].flow += res;
            if (in == 0) break;
        }
    } return out;
}

int Dinic(int _s, int _t) {
    rep(i,1,M) {
        if (i == S or i == T) continue;
        if (d[i] > 0) __Adde(S, i, d[i]);
        else if (d[i] < 0) __Adde(i, T, -d[i]);
    } 
    while(bfs(_s, _t)) dfs(_s, inf, _t);
    adde(t, s, 0, inf);
    while(bfs(_s, _t)) dfs(_s, inf, _t);
    return e[mlc].flow;
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n;
    M = n, s = ++ M, t = ++ M, S = ++ M, T = ++ M;
    rep(i,1,n) {
        adde(s, i, 0, inf), adde(i, t, 0, inf);
        cin >> t1;
        while (t1 --) {
            cin >> t2;
            adde(i, t2, 1, inf);
        }
    } 
    cout << Dinic(S, T);
}



老C的方块

除了思考难度和代码难度外没有难度的题

就是你看啊 这结构的成型是不是有点刻意?我们是不是能把每个结构都串起来?

会了吧?我们得保证没有上面那样的情况出现,也就是说求解最小割。然后这就变成了染色后分成好几层,每层间串起来求最小割的题了。怎么染色呢?看第 \(3,4\) 两个图形你大概就能构造出方法了。如下:

染色求解就行了。具体看代码吧,没什么注意的点。

注:图来自 Danno 的题解

code
#include <bits/stdc++.h>
using namespace std; using pii = pair<int,int>; using vi = vector<int>; using vp = vector<pii>; using ll = long long; 
using ull = unsigned long long; using db = double; using ld = long double; using lll = __int128_t;
mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count());
template <typename T> T rand(T l, T r) { return uniform_int_distribution<T>(l, r)(rnd); }
template <typename T1, typename T2> T1 min(T1 a, T2 b) { return a < b ? a : b; }
template <typename T1, typename T2> T1 max(T1 a, T2 b) { return a > b ? a : b; }
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define ufile(x) 
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
#define Aster(i, s) for (int i = head[s], v; i; i = e[i].next)
#define all(s) s.begin(), s.end()
#define eb emplace_back
#define pb pop_back
#define em emplace
const int N = 1e6 + 5;
const int inf = 0x3f3f3f3f;
const ll infll = 0x3f3f3f3f3f3f3f3fll;
int M, s, t, n, m, k, t1, t2, t3, t4;
int dx[] = { 0, 0, -1, 1 }, dy[] = { -1, 1, 0, 0 };
vp stk;

unordered_map <long long, pii> mp;
#define id(i, j) ( 1ll * ((i) - 1) * m + (j) )

int col(int x, int y) {
	// yellow -> blue -> green -> red
	// src -> yellow cost= yellow
	// yellow -> blue INF
	// blue -> green cost= min(blue, green)
	// green -> red   INF
	// red -> dst	 cost= red
	if (x % 4 == 1) {
		if (y & 1) return 3; // blue
		else return 4; // yellow
	} else if (x % 4 == 2) {
		if (y & 1) return 1; // green
		else return 2; // red
	} else if (x % 4 == 3) {
		if (y & 1) return 2; 
		else return 1; 
	} else {
		if (y & 1) return 4;
		else return 3;
	}
}

int head[2][N], mlc = 1;
struct ep {
    int to, next, flow;
} e[N << 1];
void adde(int u, int v, int f, bool __same = false) {
	// cout << u << " -> " << v << ' ' << f << endl;
    e[++ mlc] = { v, head[0][u], f };
    head[0][u] = mlc;
    e[++ mlc] = { u, head[0][v], __same ? f : 0 };
    head[0][v] = mlc; 
}

int dep[N]; 
bool bfs(int s, int t) {
    rep(i,1,M) dep[i] = 0, head[1][i] = head[0][i];
    queue<int> que;
    que.push(s); dep[s] = 1;
    while (que.size()) {
        int u = que.front(); que.pop();
        for (int i = head[0][u], v; i; i = e[i].next) {
            v = e[i].to;
            if (e[i].flow and !dep[v]) {
                dep[v] = dep[u] + 1;
                if (v == t) return 1;
                que.push(v);
            }
        }
    } return dep[t];
}

int dfs(int u, int in, int t) {
    if (u == t or in <= 0) return in;
    int out = 0;
    for (int &i = head[1][u], v; i; i = e[i].next) {
        v = e[i].to;
        if (e[i].flow and dep[v] == dep[u] + 1) {
            int res = dfs(v, min(in, e[i].flow), t);
            in -= res, out += res;
            e[i].flow -= res, e[i ^ 1].flow += res;
            if (in <= 0) break;
        }
    }
	if (out <= 0) dep[u] = 0;
	return out;
}

int Dinic(int s, int t) {
    int ret = 0, fl;
    while (bfs(s, t)) while (fl = dfs(s, inf, t)) ret += fl;
    return ret;
}

signed main() {
	cin >> n >> m >> k;
	s = ++ M, t = ++ M;
	rep(i,1,k) {
		cin >> t1 >> t2 >> t3;
		stk.eb(t1, t2);
		mp[id(t1, t2)] = {++ M, t3};
		t4 = col(t1, t2);
		if (t4 == 2) adde(M, t, t3);
		if (t4 == 4) adde(s, M, t3);
	}
	for (auto [x, y] : stk) rep(i,0,3) {
		int vx = x + dx[i], vy = y + dy[i];
		if (vx < 1 or n < vx or vy < 1 or m < vy) continue;
		t1 = col(x, y), t2 = col(vx, vy);
		if (t1 == 1) {
			if (t2 == 2) {
				adde(mp[id(x, y)].first, mp[id(vx, vy)].first, inf);
			} else if (t2 == 3) {
				continue;
			}
		} else if (t1 == 2) {
			if (t2 == 1) {
				continue; 
			} else if (t2 == 4) {
				continue;
			}
		} else if (t1 == 3) {
			if (t2 == 1) {
				adde(mp[id(x, y)].first, mp[id(vx, vy)].first, min(mp[id(x, y)].second, mp[id(vx, vy)].second));
			} else if (t2 == 4) {
				continue;
			}
		} else {
			if (t2 == 2) {
				continue;
			} else if (t2 == 3) {
				adde(mp[id(x, y)].first, mp[id(vx, vy)].first, inf);
			}
		}
	}
	cout << Dinic(s, t) << '\n';
}



餐巾计划问题

本人!已经拿起那块最初的餐巾了!

就是用一单位的流来模拟一块餐巾。每天拆成两个点,一个点收干净餐巾一个点发放脏餐巾。这两个点间的流量是 \([r_i, \inf]\)。然后整个源点表示买餐巾,费用是 \(p\),流量是 \([0, \inf]\)。保存就往下一天的脏餐巾连,流量 \([0, \inf]\)。快洗和慢洗就直接连对应天的干净餐巾,费用是 \(f_i/s_i\)

跑就行了。其实我写这题是这几天一看到这题就想到大总统(

code
int M, s, t, n, m, t1, t2, t3, t4;

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n;
    M = n << 1, s = ++ M, t = ++ M;
    rep(i,1,n) cin >> t1, adde(s, i + n, t1, 0), adde(i, t, t1, 0);
    cin >> t1 >> m >> t2 >> t3 >> t4;
    rep(i,1,n-1) adde(i + n, i + 1 + n, inf, 0);
    rep(i,1,n - m) adde(i + n, i + m, inf, t2);
    rep(i,1,n - t3) adde(i + n, i + t3, inf, t4);
    rep(i,1,n) adde(s, i, inf, t1);
    Dinic(s, t);
    cout << min_cost << '\n';
}
posted @ 2022-12-13 21:20  joke3579  阅读(52)  评论(0编辑  收藏  举报