[LibreOJ 2100][TJOI2015]线性代数
颓一波式子可以发现答案就是\(ABA^T-CA^T\)。然后发现对于一个\(B\)中的元素\(B_{i, j}\)要对答案做出贡献要求\(A_i\)和\(A_j\)都为1,而\(A_i\)为1会导致答案减去一个\(C_i\)。
因此我们可以分别对\(B\)中元素和\(A\)中元素建点。\(B\)中元素会带来收益,但是要求依赖两个\(A\)中元素。而\(A\)中元素会带来一定的笋丝。这个就已经事很明显的最大权闭合子图力,,,
代码:
#include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <functional> #include <utility> #include <vector> #include <queue> #include <stack> const int maxn = 505; const int maxno = 500 * 500 + 500 + 5; const int maxm = 2 * (500 * 500 * 3 + 500 + 5); int first[maxno]; int next[maxm], to[maxm], flow[maxm], cap[maxm]; int gcnt = 0; void add_edge(int u, int v, int c) { gcnt ++; next[gcnt] = first[u]; first[u] = gcnt; to[gcnt] = v; cap[gcnt] = c; flow[gcnt] = 0; } void ins_edge(int u, int v, int c) { add_edge(u, v, c); add_edge(v, u, 0); } int rev(int i) { if(1 & i) { return i + 1; } else { return i - 1; } } int d[maxno]; int s, t, num; bool bfs() { static bool vis[maxno]; std::fill(vis, vis + num + 1, false); std::fill(d, d + num + 1, 0); std::queue<int> Q; Q.push(s); d[s] = 1; vis[s] = true; while(!Q.empty()) { int u = Q.front(); Q.pop(); for(int i = first[u]; i; i = next[i]) { int v = to[i]; if(!vis[v] && cap[i] > flow[i]) { d[v] = d[u] + 1; Q.push(v); vis[v] = true; } } } return vis[t]; } int cur[maxno]; int dfs(int x, int a) { if(a == 0 || x == t) return a; int ret = 0; for(int &i = cur[x]; i; i = next[i]) { int v = to[i], f; if(d[v] == d[x] + 1 && (f = dfs(v, std::min(a, cap[i] - flow[i]))) > 0) { ret += f; a -= f; flow[i] += f; flow[rev(i)] -= f; if(a == 0) break; } } if(a > 0) d[x] = -1; return ret; } int dinic() { int ret = 0; while(bfs()) { for(int i = 0; i <= num; i ++) cur[i] = first[i]; ret += dfs(s, 0x7f7f7f7f); } return ret; } int n; int main() { int ans = 0; scanf("%d", &n); s = 0, t = n * n + n + 1; num = t; for(int i = 1; i <= n; i ++) { for(int j = 1; j <= n; j ++) { int u = (i - 1) * n + j; int val; scanf("%d", &val); ans += val; ins_edge(s, u, val); ins_edge(u, n * n + i, 0x7f7f7f7f); ins_edge(u, n * n + j, 0x7f7f7f7f); } } for(int i = 1; i <= n; i ++) { int val; scanf("%d", &val); ins_edge(n * n + i, t, val); } ans -= dinic(); printf("%d\n", ans); return 0; }
[LibreOJ 2146][SHOI2017]寿司餐厅
建了半天图……搞出来了几个神奇的最小割……
然后发现这TM不就是最简单的最大权闭合子图吗……
首先和正负点权普通的最大权闭合子图一样,然后任意\(d_{i, i}\)去依赖点\(i\)。对于一个\(d_{i, j}(i < j)\),我们要保证那一天\([i, j]\)全部被吃了,所以说它需要依赖\(d_{i + 1, j}\)和\(d_{i, j - 1}\)。
每种寿司的费用也不难处理,我们把\(mx^2\)和\(cx\)分开考虑。\(mx^2\)单独建点,很显然代号为所有\(x\)都要依赖它。对于\(cx\),可以视为所有点有一个\(x\)的费用。
然后van了……
代码:
#include <cstdio> #include <cstring> #include <cstdlib> #include <cctype> #include <cassert> #include <algorithm> #include <utility> #include <vector> #include <queue> const int maxn = 5000005; const int maxm = 5000005; int first[maxn]; int next[maxm], to[maxm], cap[maxm], flow[maxm]; int gcnt = 0; void add_edge(int u, int v, int f) { gcnt ++; next[gcnt] = first[u]; first[u] = gcnt; to[gcnt] = v; cap[gcnt] = f; flow[gcnt] = 0; } int rev(int i) { if(1 & i) { return i + 1; } else { return i - 1; } } void ins_edge(int u, int v, int f) { add_edge(u, v, f); add_edge(v, u, 0); } int s, t; int d[maxn]; bool bfs() { #ifdef LOCAL // puts("A new round :"); #endif static bool vis[maxn]; memset(vis, 0, sizeof(vis)); d[s] = 1; vis[s] = true; std::queue<int> Q; Q.push(s); while(!Q.empty()) { int u = Q.front(); Q.pop(); for(int i = first[u]; i; i = next[i]) { int v = to[i]; if(cap[i] > flow[i] && !vis[v]) { d[v] = d[u] + 1; vis[v] = true; #ifdef LOCAL // printf("d[%d] : %d\n", v, d[v]); #endif Q.push(v); } } } return vis[t]; } int cur[maxn]; int dfs(int u, int a) { #ifdef LOCAL // printf("State (%d, %d)\n", u, a); #endif if(a == 0 || u == t) return a; int fl = 0; for(int &i = cur[u]; i; i = next[i]) { int v = to[i]; int f; if(d[v] == d[u] + 1 && (f = dfs(v, std::min(cap[i] - flow[i], a))) > 0) { fl += f; a -= f; flow[i] += f; flow[rev(i)] -= f; if(a == 0) break; } } if(a > 0) d[u] = -1; return fl; } int tot; int dinic() { int ans = 0; while(bfs()) { for(int i = 0; i <= tot; i ++) cur[i] = first[i]; ans += dfs(s, 0x7fffffff); } return ans; } int ma_p[105], ma_m[1005]; int ma_d[105][105]; int main() { tot = 1; int n, m; scanf("%d%d", &n, &m); int ans = 0; s = 0, t = 1; for(int i = 1; i <= n; i ++) { int a; scanf("%d", &a); ma_p[i] = ++ tot; #ifdef LOCAL printf("p[%d] : %d\n", i, tot); #endif if(!ma_m[a]) { ma_m[a] = ++ tot; #ifdef LOCAL printf("m[%d] : %d\n", a, tot); #endif ins_edge(tot, 1, m * a * a); } ins_edge(ma_p[i], 1, a); ins_edge(ma_p[i], ma_m[a], 0x7fffffff); } for(int i = 1; i <= n; i ++) { ma_d[i][i] = ++ tot; for(int j = i + 1; j <= n; j ++) { ma_d[i][j] = ++ tot; #ifdef LOCAL printf("ma_d[%d][%d] : %d\n", i, j, tot); #endif } } for(int i = 1; i <= n; i ++) { int d_i; scanf("%d", &d_i); if(d_i >= 0) { ans += d_i; ins_edge(0, ma_d[i][i], d_i); } else { ins_edge(ma_d[i][i], 1, -d_i); } ins_edge(ma_d[i][i], ma_p[i], 0x7fffffff); for(int j = i + 1; j <= n; j ++) { scanf("%d", &d_i); int np = ma_d[i][j]; if(d_i >= 0) { ans += d_i; ins_edge(0, np, d_i); ins_edge(np, ma_d[i][j - 1], 0x7fffffff); ins_edge(np, ma_d[i + 1][j], 0x7fffffff); } else { ins_edge(np, 1, -d_i); ins_edge(np, ma_d[i][j - 1], 0x7fffffff); ins_edge(np, ma_d[i + 1][j], 0x7fffffff); } } } ans -= dinic(); #ifdef LOCAL for(int i = 0; i <= tot; i ++) { for(int j = first[i]; j; j = next[j]) { if(!(j & 1)) continue; int v = to[j]; if(cap[j] == flow[j]) { printf("Edge (%d, %d, %d) deleted.\n", i, v, cap[j]); } } } #endif printf("%d\n", ans); return 0; }