学习小组 洛谷P4209

原题

这是一道比较优秀的技巧类题目。

我们通过观察数据范围,发现很小,首先排除贪心。

由于题目限制多而复杂,我们考虑使用网络流来描述这个问题。

考虑建立一个网络流框架,大概是我们要求每个能选小组的学生都至少选一个小组,可以使用最大流描述,但是题目有要求支出最少,因此考虑使用费用流进一步描述。

先考虑如何描述参加了一些学生的小组的支出,我们可以发现由于支出增长是平方倍级别的,因此无法直接用同一的费用描述。我们发现由于 \(C,F\) 是正数,因此不难证明有以下式子:

\[C_i \times (a+1)^2 - F_i \times (a+1) - C_i \times a^2 + F_i \times a > C_i \times a^2 - F_i \times a - C_i \times (a-1)^2 + F_i \times (a-1) \]

即相邻参加人数增加所带来的花费变化量是单调递增的,我们考虑把他的花费进行差分,拆成 \(n\) 条边与汇点相连,根据贪心想法,跑费用流时一定会优先选较小的边,因此正确性可以保证。

另外,考虑如何限制每个学生至少选择一个。根据贪心想法,由于询问最少支出,我们考虑在什么时候学生会选择不止一个学习小组。显然,当且仅当多选择小组会让花费更小时我们才会多选,为了保证学生至少选一个我们考虑使用满流的方式限制。因此我们可以从每个学生往汇点连接一条容量为 \(k - 1\) ,花费为 \(0\) 的边,就可以限制这个学生至少选择一个小组,同时想要多选则花费必须比 \(0\) 更小。

至此,我们的建图满足了题目的所有限制条件,代码如下:

#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;

template <typename T>
void read(T &x) {
	T f=1;x=0;char s=getchar();
	while(s<'0'||s>'9') {if(s=='-') f=-1;s=getchar();}
	while(s>='0'&&s<='9') {x=(x<<3)+(x<<1)+(s^'0');s=getchar();}
	x *= f;
}

const int MAXN = 1e5 + 5;
const int MAXM = 1e5 + 5;
const LL inf = 1e15;

int head[MAXN] , to[MAXM << 1] , nxt[MAXM << 1] , cnt = 1;
LL edge[MAXM << 1] , val[MAXM << 1];
void add(int u , int v , LL c , LL w) {
	nxt[++cnt] = head[u];head[u] = cnt;to[cnt] = v;edge[cnt] = c;val[cnt] = w;
	nxt[++cnt] = head[v];head[v] = cnt;to[cnt] = u;edge[cnt] = 0;val[cnt] = -w;
}

int las[MAXN] , pre[MAXN] , num , s , t , vis[MAXN];
LL dis[MAXN] , flow[MAXN];

struct MinCostMaxFlow {
	LL MaxFlow , MinCost;
	bool bfs() {
		for (int i = 1; i <= num; ++i) las[i] = 0 , dis[i] = inf , pre[i] = 0 , flow[i] = 0 , vis[i] = 0;
		flow[s] = inf , dis[s] = 0;
		queue <int> q;q.push(s);
		int flag = 0;
		while(!q.empty()) {
			int x = q.front();
			q.pop();
			vis[x] = 0;
			for (int i = head[x]; i; i = nxt[i]) {
				if(!edge[i]) continue;
				int v = to[i];
				if(dis[v] > dis[x] + val[i]) {
					dis[v] = dis[x] + val[i];
					flow[v] = min(flow[x] , edge[i]);
					pre[v] = x;
					las[v] = i;
					if(v == t) {
						flag = 1;
						continue;
					}
					if(!vis[v]) vis[v] = 1 , q.push(v);
				}
			}
		} 
		return flag;
	}
	void MVMC() {
		MaxFlow = 0 , MinCost = 0;
		while(bfs()) {
			int now = t;
			MaxFlow += flow[t];
			MinCost += dis[t] * flow[t];
			while(now != s) {
				edge[las[now]] -= flow[t];
				edge[las[now] ^ 1] += flow[t];
				now = pre[now];
			}
		}
	} 
}MIN;

int n , m , k , id[MAXN] , C[MAXN];
int per[MAXN];

int main() {
	read(n),read(m),read(k);
	s = 1 , t = 2 , num = 2;
	for (int i = 1; i <= m; ++i) {
		read(C[i]);
		id[i] = ++num;
	}
	for (int i = 1; i <= m; ++i) {
		int F;
		read(F);
		for (int j = 1; j <= n; ++j) add(id[i] , t , 1 , C[i] * (2 * j - 1) - F);
	}
	for (int i = 1; i <= n; ++i) {
		per[i] = ++num;
		char s[105];
		scanf("%s" , s + 1);
		for (int j = 1; j <= m; ++j) {
			if(s[j] == '1') add(per[i] , id[j] , 1 , 0);
		}
	}
	for (int i = 1; i <= n; ++i) {
		add(s , per[i] , k , 0);
		add(per[i] , t , k - 1 , 0);
	}
	MIN.MVMC();
	printf("%lld" , MIN.MinCost);
	return 0;
}
posted @ 2021-01-05 21:33  Reanap  阅读(50)  评论(0编辑  收藏  举报