题解:SP4063 MPIGS - Sell Pigs

涉及知识点:网络流

解题思路

根据直觉,本题的可行流是猪。大致的网络是猪从猪舍流到顾客手里。

建立流网络

由于有多个猪舍和多名顾客,所以原问题需要建立超级源点和超级汇点。

对于超级源点,需要向每一个猪舍连一条容量为猪舍猪的数量的边。

对于超级汇点,需要顾客节点向超级汇点连一条容量为顾客最多买的猪的数量的边。

对于猪舍和顾客,如果第 \(i\) 号顾客有第 \(j\) 号猪舍的钥匙,那么就连一条从 \(j\)\(i\) 的边,这些边的容量是没有任何限制的,所以为正无穷。

\(2\) 个操作的建立过程会在以下介绍。

更简单的问题

先思考如果没有第 \(2\) 个操作,本问题应该如何解决。

显然,问题变成了一个二分图的多重匹配问题,直接跑一遍最大可行流的模板即可。

原问题

考虑如何把第 \(2\) 个操作也放到流网络里,也就是如何把猪的转移描述出来。

错误的做法

猪的转移相当于把全部猪给顾客,再把猪重新分配给顾客可以打开的猪舍,所以需要再从顾客向可以打卡的猪舍连一条容量为正无穷的边。

这样就可以体现出顾客打开一些猪舍,把猪舍内部的猪进行调整。

但是这样做还是有一个问题,它会导致我们失去时间顺序这个概念,它会把所以顾客看做同时到来,顾客到来的顺序就体现不出来了,所以这样也是不对的。

正确的做法

如果想体现时间顺序的概率,我们可以考虑猪只能从前面一个顾客转移的结果流到当前顾客,不能把后面顾客的结果流到当前顾客。

所以说,这就提示我们,在建边的时候,能不能把猪的节点删除掉,把所有的连边看成顾客之间的连边,然后,顾客的连边保证一定是从先到的顾客连到后到的顾客,这样的可以保留时间顺序了。

思考如何这样解决本题。

注:前面建的流网络全部作废,也就是没用了,但是源点和汇点还是需要的。

考虑第 \(i\) 个顾客,他的猪的来源一定是从他有钥匙的猪舍过来的,假设他有 \(k\) 个猪舍的钥匙。

设当前猪舍的编号为 \(j\)

对于每个猪舍,有以下两种可能:

  1. 如果猪舍 \(j\) 以前没有被任何一个顾客打开过,所以从猪舍 \(j\) 到当前顾客的猪一定是初始的猪,就可以从源点向当前顾客连一条容量为猪舍 \(j\) 里面猪的数量的边。
  2. 如果猪舍 \(j\) 不是第 \(1\) 次被打开,设猪舍 \(j\) 被顾客 \(x\) 打开过,所以猪舍 \(j\) 里面的猪要么是被顾客 \(x\) 挑剩下的,要么是顾客 \(x\) 可以打开的另外一些猪舍里的猪,总之,猪舍 \(j\) 里的猪是经过顾客 \(x\) 流过来的,对于这种猪舍,直接连一条从顾客 \(x\) 到顾客 \(i\) 容量为正无穷的边。

当然,每个顾客需要像之前那样向汇点连一条容量为最多购买猪的数量的边。

这样,我们的流网络就建完了。

代码

#include <bits/stdc++.h>
#define int long long
#define ll __int128
#define db double
#define ldb long double
#define vo void
#define endl '\n'
#define il inline
#define re register
#define ve vector
#define p_q priority_queue
#define PII pair<int, int>
#define u_m unordered_map
#define bt bitset
#define iv il void
#define ii il int
#define ib il bool
#define ri register int

using namespace std;

using i64 = long long;

//#define O2 1
#ifdef O2
	#pragma GCC optimize(1)
	#pragma GCC optimize(2)
	#pragma GCC optimize(3, "Ofast","inline")
#endif

#define use_fast

namespace IO {
#define IOSIZE 100000
	int precision = 3, POW[10] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
	char ibuf[IOSIZE], obuf[IOSIZE], *p1 = ibuf, *p2 = ibuf, *p3 = obuf;
#ifdef ONLINE_JUDGE
	#define getchar()((p1==p2)and(p2=(p1=ibuf)+fread(ibuf,1,IOSIZE,stdin),p1==p2)?(EOF):(*p1++))
	#define putchar(x)((p3==obuf+IOSIZE)&&(fwrite(obuf,p3-obuf,1,stdout),p3=obuf),*p3++=x)
	#define isdigit(ch)(ch>47&&ch<58)
	#define isspace(ch)(ch<33)
#endif
	template<typename T>inline T read() {
		T s = 0;
		int w = 1;
		char ch;
		while (ch = getchar(), !isdigit(ch) && (ch != EOF))if (ch == 45)w = -1;
		if (ch == EOF)return 0;
		while (isdigit(ch))s = s * 10 + ch - 48, ch = getchar();
		return s * w;
	} template<typename T>inline bool read(T&s) {
		s = 0;
		int w = 1;
		char ch;
		while (ch = getchar(), !isdigit(ch) && (ch != EOF))if (ch == 45)w = -1;
		if (ch == EOF)return 0;
		while (isdigit(ch))s = s * 10 + ch - 48, ch = getchar();
		return s *= w, 1;
	} inline bool read(char&s) {
		while (s = getchar(), isspace(s));
		return 1;
	} inline bool read(char*s) {
		char ch;
		while (ch = getchar(), isspace(ch));
		if (ch == EOF)return 0;
		while (!isspace(ch))*s++ = ch, ch = getchar();
		*s = '\000';
		return 1;
	} template<typename T>inline void print(T x) {
		if (x < 0)putchar(45), x = -x;
		if (x > 9)print(x / 10);
		putchar(x % 10 + 48);
	} inline void print(char x) {
		putchar(x);
	} inline void print(char*x) {
		while (*x)putchar(*x++);
	} inline void print(const char*x) {
		for (int i = 0; x[i]; i++)putchar(x[i]);
	} inline bool read(std::string&s) {
		s = "";
		char ch;
		while (ch = getchar(), isspace(ch));
		if (ch == EOF)return 0;
		while (!isspace(ch))s += ch, ch = getchar();
		return 1;
	} inline void print(std::string x) {
		for (int i = 0, n = x.size(); i < n; i++)putchar(x[i]);
	} inline bool read(bool&b) {
		char ch;
		while (ch = getchar(), isspace(ch));
		b = ch ^ 48;
		return 1;
	} inline void print(bool b) {
		putchar(b + 48);
	} inline bool read(double&x) {
		int a = 0, b = 0;
		char ch = getchar();
		bool f = 0;
		while (ch < 48 || ch > 57) {
			if (ch == 45)f = 1;
			ch = getchar();
		}
		while (47 < ch && ch < 58) {
			a = (a << 1) + (a << 3) + (ch ^ 48);
			ch = getchar();
		}
		if (ch != 46) {
			x = f ? -a : a;
			return 1;
		}
		ch = getchar();
		while (47 < ch && ch < 58) {
			b = (b << 1) + (b << 3) + (ch ^ 48), ch = getchar();
		}
		x = b;
		while (x > 1)x /= 10;
		x = f ? -a - x : a + x;
		return 1;
	} inline void print(db x) {
		if (x < 0) {
			putchar(45), x = -x;
		}
		x = round((ldb)x * POW[precision]) / POW[precision], print((int)x), putchar(46), x -= (int)(x);
		for (int i = 1; i <= precision; i++)x *= 10, putchar(x + 48), x -= (int)x;
	} template<typename T, typename...T1>inline int read(T&a, T1&...other) {
		return read(a) + read(other...);
	} template<typename T, typename...T1>inline void print(T a, T1...other) {
		print(a), print(other...);
	} struct Fast_IO {
		~Fast_IO() {
			fwrite(obuf, p3 - obuf, 1, stdout);
		}
	} io;
	template<typename T>Fast_IO&operator>>(Fast_IO&io, T&b) {
		return read(b), io;
	} template<typename T>Fast_IO&operator<<(Fast_IO&io, T b) {
		return print(b), io;
	}
#ifdef use_fast
	#define cout io
	#define cin io
#endif
}

const int inf = 1e8;
const db PI = acos(-1);

namespace Template {
	int fact[200000];
	int Triangle[1010][1010];
	int Prime[2000000], Prime_vis[2000000];
	int Prime_len;
	void Fact(int n, int mod) {
		fact[0] = 1;
		for (int i = 1; i <= n; i ++ ) fact[i] = ((fact[i - 1]) % mod * (i % mod)) % mod;
	}
	void Pascal_s_triangle(int n, int mod) {
		for (int i = 0; i <= n; i ++ ) Triangle[i][0] = 1;
		for (int i = 1; i <= n; i ++ )
			for (int j = 1; j <= i; j ++ )
				Triangle[i][j] = (Triangle[i - 1][j] + Triangle[i - 1][j - 1]) % mod;
	}
	void Get_prime(int n) {
		for (int i = 2; i <= n; i ++ ) {
			if (!Prime_vis[i]) Prime[++Prime_len] = i;
			for (int j = 1; Prime[j] * i <= n; j ++ ) {
				Prime_vis[Prime[j] * i] = 1;
				if (i % Prime[j] == 0) break;
			}
		}
	}
	int pw(int x, int y, int mod) {
		int res = 1;
		while (y) {
			if (y & 1) res = ((res % mod) * (x % mod)) % mod;
			x = (x % mod) * (x % mod) % mod;
			y >>= 1;
		}
		return res;
	}
	int pw(int x, int y) {
		int res = 1;
		while (y) {
			if (y & 1) res *= x;
			x *= x;
			y >>= 1;
		}
		return res;
	}
	int GCD(int x, int y, int mod) {
		return __gcd(x, y) % mod;
	}
	int LCM(int x, int y, int p) {
		return x * y % p * pw(GCD(x, y, p), p - 2, p) % p;
	}
	int C(int n, int m, int mod) {
		if (m > n || m < 0) return 0;
		return fact[n] * pw(fact[m], mod - 2, mod) % mod * pw(fact[n - m], mod - 2, mod) % mod;
	}
	il int Lucas(int n, int m, int p) {
		if (!m) return 1;
		return (C(n % p, m % p, p) * Lucas(n / p, m / p, p)) % p;
	}
	int Ask_triangle(int x, int y) {
		return Triangle[x][y];
	}
	il int BSGS(int a, int b, int p) {
		if (1 % p == b % p) return 0;
		int k = sqrt(p) + 1;
		u_m<int, int> Hash;
		for (int i = 0, j = b % p; i < k; i ++ ) {
			Hash[j] = i;
			j = j * a % p;
		}
		int ak = 1;
		for (int i = 0; i < k; i ++ ) ak = ak * a % p;
		for (int i = 1, j = ak; i <= k; i ++ ) {
			if (Hash.count(j)) return i * k - Hash[j];
			j = j * ak % p;
		}
		return -inf;
	}
	il int exgcd(int a, int b, int &x, int &y) {
		if (!b) {
			x = 1, y = 0;
			return a;
		}
		int d = exgcd(b, a % b, y, x);
		y -= a / b * x;
		return d;
	}
	il int exBSGS(int a, int b, int p) {
		b = (b % p + p) % p;
		if (1 % p == b % p) return 0;
		int x, y;
		int d = exgcd(a, p, x, y);
		if (d > 1) {
			if (b % d) return -inf;
			exgcd(a / d, p / d, x, y);
			return exBSGS(a, b / d * x % (p / d), p / d) + 1;
		}
		return BSGS(a, b, p);
	}
	il int inv(int a, int p) {
		return pw(a, p - 2, p);
	}
}
using namespace Template;

#ifdef use_fast
	using namespace IO;
#else
	#define IOS 1
#endif

//#define fre 1
//#define multitest 1

const int N = 100 + 10;
const int M = 20200 + 10;
const int Mod = 1e9 + 9;

namespace zla {
	int n, m;
	int S, T;
	int h[N], e[M], f[M], ne[M], idx;
	int q[N], d[N], cur[N];
	int w[1010], last[1010];

	void add(int a, int b, int c) {
		e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
		e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
	}

	bool bfs() {
		int hh = 0, tt = 0;
		memset(d, -1, sizeof d);
		q[0] = S, d[S] = 0, cur[S] = h[S];
		while (hh <= tt) {
			int t = q[hh ++ ];
			for (int i = h[t]; ~i; i = ne[i]) {
				int ver = e[i];
				if (d[ver] == -1 && f[i]) {
					d[ver] = d[t] + 1;
					cur[ver] = h[ver];
					if (ver == T) return true;
					q[ ++ tt] = ver;
				}
			}
		}
		return false;
	}

	int find(int u, int limit) {
		if (u == T) return limit;
		int flow = 0;
		for (int i = cur[u]; ~i && flow < limit; i = ne[i]) {
			cur[u] = i;
			int ver = e[i];
			if (d[ver] == d[u] + 1 && f[i]) {
				int t = find(ver, min(f[i], limit - flow));
				if (!t) d[ver] = -1;
				f[i] -= t, f[i ^ 1] += t, flow += t;
			}
		}
		return flow;
	}

	int Dinic() {
		int r = 0, flow;
		while (bfs()) while (flow = find(S, inf)) r += flow;
		return r;
	}

	il void Init() {
		cin >> m >> n;
		S = 0, T = n + 1;
		memset(h, -1, sizeof h);
		for (int i = 1; i <= m; i ++ ) cin >> w[i];
		for (int i = 1; i <= n; i ++ ) {
			int a, b;
			cin >> a;
			while (a -- ) {
				int id;
				cin >> id;
				if (!last[id]) add(S, i, w[id]);
				else add(last[id], i, inf);
				last[id] = i;
			}
			cin >> b;
			add(i, T, b);
		}
	}

	il void Solve() {
		cout << Dinic();
	}

	il void main() {
		Init();
		Solve();
	}
}

il void Init() {
	;
}

signed main() {
	int T;
#ifdef IOS
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#endif
#ifdef fre
	freopen(".in", "r", stdin);
	freopen(".out", "w", stdout);
#endif
	Init();
#ifdef multitest
	cin >> T;
#else
	T = 1;
#endif
	while (T--) zla::main();
	return 0;
}
/*

*/
posted @ 2024-10-25 14:42  zla_2012  阅读(2)  评论(0编辑  收藏  举报