「題解」洛谷 P6054 [RC-02] 開門大吉
阿新 • • 發佈:2022-01-17
一套題裡面的各個題是假的,可以處理出 \(w_{i,j}\) 為第 \(i\) 個人選第 \(j\) 套題的期望得分。
對於每個人來講,有 \(m\) 種選擇套題,只能選擇一個套題,要求價值最小,於是想到一個最小割的經典建圖:對於每個人,從 \(S\) 到 \(T\) 連一條長度為 \(m\) 的鏈,邊的流量依次為這個人選擇第 \(i\) 套題的期望得分。
考慮怎樣描述限制,若割了 \(j\) 的第 \(l\) 條邊,那麼就不能割 \(i\) 的前 \((j+k-1)\) 條邊。
那麼就從 \(j\) 的第 \(l\) 條邊的起點,向 \(i\) 的前 \((j+k-1)\) 條邊的終點連一條 \(+\infty\)
但是要注意到,限制和限制之間是有傳遞性的,假若有 \(1\) 比 \(2\) 大 \(1\),\(2\) 比 \(3\) 大 \(1\),那麼還需要滿足 \(1\) 比 \(3\) 大 \(2\).
將一開始 \(S\) 到 \(T\) 的若干條長度為 \(m\) 的鏈,連一條反向的流量為 \(+\infty\) 的邊,這樣限制和限制組成的限制也被滿足了。
最小割 建圖模型:每個位置 \(n\) 選 \(1\),求最小代價,\(S\) 到 \(T\) 連若干條長度為 \(n\),流量依次為每種選擇的代價,求最小割。
#include<iostream> #include<cstdio> #include<algorithm> #include<vector> #include<queue> #define pb emplace_back #define mp std::make_pair #define fi first #define se second typedef long long ll; typedef long double ld; typedef unsigned long long ull; typedef std::pair<int, int> pii; typedef std::vector<int> vi; const ll mod = 998244353; ll Add(ll x, ll y) { return (x+y>=mod) ? (x+y-mod) : (x+y); } ll Mul(ll x, ll y) { return x * y % mod; } ll Mod(ll x) { return x < 0 ? (x + mod) : (x >= mod ? (x-mod) : x); } ll cadd(ll &x, ll y) { return x = (x+y>=mod) ? (x+y-mod) : (x+y); } ll cmul(ll &x, ll y) { return x = x * y % mod; } template <typename T> T Max(T x, T y) { return x > y ? x : y; } template<typename T, typename... T2> T Max(T x, T2 ...y) { return Max(x, y...); } template <typename T> T Min(T x, T y) { return x < y ? x : y; } template<typename T, typename... T2> T Min(T x, T2 ...y) { return Min(x, y...); } template <typename T> T cmax(T &x, T y) { return x = x > y ? x : y; } template <typename T> T cmin(T &x, T y) { return x = x < y ? x : y; } template <typename T> T &read(T &r) { r = 0; bool w = 0; char ch = getchar(); while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar(); while(ch >= '0' && ch <= '9') r = r * 10 + (ch ^ 48), ch = getchar(); return r = w ? -r : r; } template<typename T1, typename... T2> void read(T1 &x, T2& ...y) { read(x); read(y...); } const int N = 100010; const int M = 500010; const ll INF = 0x7fffffffffffffff; const ld eps = 1e-10; int n, m, p, y, c[N], pos[81][81]; ld f[81][81][81], w[81][81]; int tot, S, T, ent = 1, head[N], cur[N], dis[N]; struct Edge { int to, nxt; ld fl; }e[M]; inline void add(int x, int y, ld z) { e[++ent].to = y; e[ent].fl = z; e[ent].nxt = head[x]; head[x] = ent; e[++ent].to = x; e[ent].fl = 0; e[ent].nxt = head[y]; head[y] = ent; } bool bfs() { for(int i = 1; i <= tot; ++i) dis[i] = -1, cur[i] = head[i]; std::queue<int>q; q.push(S); dis[S] = 0; while(!q.empty()) { int x = q.front(); q.pop(); for(int i = head[x]; i; i = e[i].nxt) { int v = e[i].to; if(dis[v] == -1 && e[i].fl > eps) { dis[v] = dis[x] + 1; q.push(v); } } } return dis[T] != -1; } bool qwqbfs() { for(int i = 1; i <= tot; ++i) dis[i] = -1; std::queue<int>q; q.push(S); dis[S] = 0; while(!q.empty()) { int x = q.front(); q.pop(); for(int i = head[x]; i; i = e[i].nxt) { int v = e[i].to; if(dis[v] == -1 && e[i].fl == INF) { dis[v] = dis[x] + 1; q.push(v); } } } return dis[T] != -1; } ld dfs(int x, ld lim) { if(x == T) return lim; ld flow = 0; for(int i = cur[x]; i && flow < lim; i = e[i].nxt) { int v = e[i].to; cur[x] = i; if(dis[v] == dis[x] + 1 && e[i].fl > eps) { ld f = dfs(v, Min(e[i].fl, lim - flow)); flow += f; e[i].fl -= f; e[i^1].fl += f; } } return flow; } ld dinic() { ld mxfl = 0; while(bfs()) mxfl += dfs(S, INF); return mxfl; } void solve() { for(int i = 1; i <= tot; ++i) head[i] = 0; tot = 0; ent = 1; read(n, m, p, y); for(int i = 1; i <= p; ++i) read(c[i]); for(int j = 1; j <= m; ++j) for(int i = 1; i <= n; ++i) for(int k = 1; k <= p; ++k) { double qwq; scanf("%lf", &qwq); f[i][j][k] = qwq; } for(int i = 1; i <= n; ++i) for(int j = 1; j <= m; ++j) { ld t = 1; w[i][j] = 0; for(int k = 1; k <= p; ++k) { t *= f[i][j][k]; w[i][j] += t * c[k]; } } S = ++tot; T = ++tot; for(int i = 1; i <= n; ++i) { pos[i][0] = S; for(int j = 1; j <= m; ++j) { if(j < m) pos[i][j] = ++tot; else pos[i][j] = T; add(pos[i][j-1], pos[i][j], w[i][j]); add(pos[i][j], pos[i][j-1], INF); } } for(int o = 1; o <= y; ++o) { int i, j, k; read(i, j, k); for(int a = 1; a <= m; ++a) if(a-1+k >= 1 && a-1+k <= m) add(pos[j][a-1], pos[i][a-1+k], INF); } if(qwqbfs()) { puts("-1"); return ; } double qwq = dinic(); printf("%lf\n", qwq); } signed main() { int T; read(T); while(T--) solve(); return 0; }