【题解】[六省联考 2017] 寿司餐厅

发布时间 2023-06-14 16:40:30作者: linyihdfj

题目描述:

Kiana 最近喜欢到一家非常美味的寿司餐厅用餐。

每天晚上,这家餐厅都会按顺序提供 \(n\) 种寿司,第 \(i\) 种寿司有一个代号 \(a_i\) 和美味度 \(d_{i, i}\),不同种类的寿司有可能使用相同的代号。每种寿司的份数都是无限的,Kiana 也可以无限次取寿司来吃,但每种寿司每次只能取一份,且每次取走的寿司必须是按餐厅提供寿司的顺序连续的一段,即 Kiana 可以一次取走第 \(1, 2\) 种寿司各一份,也可以一次取走第 \(2, 3\) 种寿司各一份,但不可以一次取走第 \(1, 3\) 种寿司。

由于餐厅提供的寿司种类繁多,而不同种类的寿司之间相互会有影响:三文鱼寿司和鱿鱼寿司一起吃或许会很棒,但和水果寿司一起吃就可能会肚子痛。因此,Kiana 定义了一个综合美味度 \(d_{i, j} \ (i < j)\),表示在一次取的寿司中,如果包含了餐厅提供的从第 \(i\) 份到第 \(j\) 份的所有寿司,吃掉这次取的所有寿司后将获得的额外美味度。由于取寿司需要花费一些时间,所以我们认为分两次取来的寿司之间相互不会影响。注意在吃一次取的寿司时,不止一个综合美味度会被累加,比如若 Kiana 一次取走了第 \(1, 2, 3\) 种寿司各一份,除了 \(d_{1, 3}\) 以外,\(d_{1, 2}, d_{2, 3}\) 也会被累加进总美味度中。

神奇的是,Kiana 的美食评判标准是有记忆性的,无论是单种寿司的美味度,还是多种寿司组合起来的综合美味度,在计入 Kiana 的总美味度时都只会被累加一次。比如,若 Kiana 某一次取走了第 \(1, 2\) 种寿司各一份,另一次取走了第 \(2, 3\) 种寿司各一份,那么这两次取寿司的总美味度为 \(d_{1, 1} + d_{2, 2} + d_{3, 3} + d_{1, 2} + d_{2, 3}\),其中 \(d_{2, 2}\) 只会计算一次。

奇怪的是,这家寿司餐厅的收费标准很不同寻常。具体来说,如果 Kiana 一共吃过了 \(c \ (c > 0)\) 代号为 \(x\) 的寿司,则她需要为这些寿司付出 \(mx^2 + cx\) 元钱,其中 \(m\) 是餐厅给出的一个常数。

现在 Kiana 想知道,在这家餐厅吃寿司,自己能获得的总美味度(包括所有吃掉的单种寿司的美味度和所有被累加的综合美味度)减去花费的总钱数的最大值是多少。由于她不会算,所以希望由你告诉她。

对于所有数据,保证 \(-500 \leq d_{i, j} \leq 500,n \le 100,a_i \le 1000,m \in \{0,1\}\)

题目分析:

看到这个 \(d_{i,j}\) 就很有支配的味道,也就是如果要有 \(d_{i,j}\) 的贡献,则必定会有 \(d_{i+1,j}\)\(d_{i,j-1}\) 的贡献。
其实就是一个选 X 则必须选 Y 的形式,也就是最大权闭合子图问题,这个看上去就很有前途,所以考虑怎么扩展到解决 \(mx^2 + cx\) 的代价,也就是怎么转化为选 X 则必选 Y 的形式。
对于 \(cx\) 的代价可以理解为每次选择类型为 \(x\) 的物品需要 \(x\) 的代价,也就是我们选择 \(d_{i,i}\) 就意味着要付出 \(a_i\) 的代价。
而对于 \(mx^2\) 的代价,因为也是只会产生一次贡献,所以我们可以考虑对于每一个类型建一个点,然后就是意味着选 \(d_{i,i}\) 必须选 \(a_i\)
最后直接跑一下最大权闭合子图的模板就好了。

代码:

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5+5;
const int INF = 1e16+5;
struct edge{
	int nxt,to,val;
	edge(){}
	edge(int _nxt,int _to,int _val){
		nxt = _nxt,to = _to,val = _val;
	}
}e[N];
int n,m,s,t,cnt = 1,head[N],cur[N],dep[N],tot,a[N],num[1005][1005],f[1005][1005];
bool vis[N];
void add_edge(int from,int to,int val){
	e[++cnt] = edge(head[from],to,val);
	head[from] = cnt;
	e[++cnt] = edge(head[to],from,0);
	head[to] = cnt;
}
bool bfs(){
	memset(vis,false,sizeof(vis));memset(dep,0,sizeof(dep));memcpy(cur,head,sizeof(head));
	dep[s] = 1;
	queue<int> q;q.push(s);
	while(!q.empty()){
		int now = q.front();q.pop();
		if(vis[now])	continue;
		vis[now] = true;
		for(int i = head[now]; i;i = e[i].nxt){
			int to = e[i].to;
			if(e[i].val && !dep[to]){
				dep[to] = dep[now] + 1;
				q.push(to);
			}
		}
	}
	return dep[t] != 0;
}
int dfs(int now,int limit){
	if(now == t)	return limit;
	int flow = 0;
	for(int i = cur[now]; flow < limit && i; i = e[i].nxt){
		cur[now] = i;
		int to = e[i].to;
		if(dep[to] == dep[now] + 1 && e[i].val){
			int h = dfs(to,min(limit - flow,e[i].val));
			e[i].val -= h;e[i^1].val += h;flow += h;
			if(!h)	dep[to] = INF;
		}
	}
	return flow;
}
int dinic(){
	int ans = 0,flow;
	while(bfs()){
		while(flow = dfs(s,INF)){
			ans += flow;
		}
	}
	return ans;
}
signed main(){
	scanf("%lld%lld",&n,&m);
	s = 1,t = 2;tot = 2;
	int mx = 0;
	for(int i=1; i<=n; i++)	scanf("%lld",&a[i]),mx = max(mx,a[i]);
	for(int i=1; i<=n; i++){
		for(int j=i; j<=n; j++){
			scanf("%lld",&f[i][j]);
			num[i][j] = ++tot;
		}
	}
	int tmp = 0;
	for(int i=1; i<=n; i++){
		for(int j=i; j<=n; j++){
			int cst = f[i][j];
			if(i == j){
				if(m)	add_edge(num[i][j],tot + a[i],INF);
				cst -= a[i];
			}
			else{
				add_edge(num[i][j],num[i+1][j],INF);
				add_edge(num[i][j],num[i][j-1],INF);
			}
			if(cst > 0)		add_edge(1,num[i][j],cst),tmp += cst;
			if(cst < 0)		add_edge(num[i][j],2,-cst);
		}
	}
	if(m)	for(int i=1; i<=mx; i++)	add_edge(++tot,2,i * i);
	printf("%lld\n",tmp - dinic());
	return 0;
}