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

题目描述

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

输入格式

第一行包含两个正整数 \(n, m\),分别表示这家餐厅提供的寿司总数和计算寿司价格中使用的常数。
第二行包含 \(n\) 个正整数,其中第 \(k\) 个数 \(a_k\) 表示第 \(k\) 份寿司的代号。
接下来 \(n\) 行,第 \(i\) 行包含 \(n - i + 1\) 个整数,其中第 \(j\) 个数 \(d_{i, i+j-1}\) 表示吃掉寿司能获得的相应的美味度,具体含义见问题描述。

输出格式

输出共一行包含一个正整数,表示 Kiana 能获得的总美味度减去花费的总钱数的最大值。

样例 #1

样例输入 #1

3 1
2 3 2
5 -10 15
-10 15
15

样例输出 #1

12

样例 #2

样例输入 #2

5 0
1 4 1 3 4
50 99 8 -39 30
68 27 -75 -32
70 24 72
-10 81
-95

样例输出 #2

381

样例 #3

样例输入 #3

10 1
5 5 4 4 1 2 5 1 5 3
83 91 72 29 22 -5 57 -14 -36 -3
-11 34 45 96 32 73 -1 0 29
-48 68 44 -5 96 66 17 74
88 47 69 -9 2 25 -49
86 -9 -77 62 -10 -30
2 40 95 -74 46
49 -52 2 -51
-55 50 -44
72 22
-68

样例输出 #3

1223

提示

样例解释 1

在这组样例中,餐厅一共提供了 \(3\) 份寿司,它们的代号依次为 \(a_1 = 2, a_2 = 3, a_3 = 2\),计算价格时的常数 \(m = 1\)

在保证每次取寿司都能获得新的美味度的前提下,Kiana 一共有 \(14\) 种不同的吃寿司方案。以下列出其中几种:

  1. Kiana 一个寿司也不吃,这样她获得的总美味度和花费的总钱数都是 \(0\),两者相减也是 \(0\)
  2. Kiana 只取 \(1\) 次寿司,且只取第 \(1\) 个寿司,即她取寿司的情况为 \(\{[1, 1]\}\),这样获得的总美味度为 \(5\),花费的总钱数为 \(1 \times 2^2 + 1 \times 2 = 6\),两者相减为 \(-1\)
  3. Kiana 取 \(2\) 次寿司,第一次取第 \(1, 2\) 个寿司,第二次取第 \(2, 3\) 个寿司,即她取寿司的情况为 \(\{[1, 2], [2, 3]\}\),这样获得的总美味度为 \(5 + (-10) + 15 + (-10) + 15 = 15\),花费的总钱数为 \((1 \times 2^2 + 2 \times 2) + (1 \times 3^2 + 1 \times 3) = 20\),两者相减为 \(-5\)
  4. Kiana 取 \(2\) 次寿司,第一次取第 \(1\) 个寿司,第二次取第 \(3\) 个寿司,即她取寿司的情况为 \(\{[1, 1], [3, 3]\}\),这样获得的总美味度为 \(5 + 15 = 20\),花费的总钱数为 \(1 \times 2^2 + 2 \times 2 = 8\),两者相减为 \(12\)

\(14\) 种方案中,惟一的最优方案是列出的最后一种方案,这时她获得的总美味度减去花费的总钱数的值最大为 \(12\)

数据范围

对于所有数据,保证 \(-500 \leq d_{i, j} \leq 500\)

数据的一些特殊约定如下表:

Case # \(n\) \(a_i\) \(m\) 附加限制
1 \(\leq 2\) \(\leq 30\) \(= 0\) -
2 \(\leq 2\) \(\leq 30\) \(= 1\) -
3 \(\leq 3\) \(\leq 30\) \(= 0\) -
4 \(\leq 3\) \(\leq 30\) \(= 1\) -
5 \(\leq 5\) \(\leq 30\) \(= 0\) -
6 \(\leq 5\) \(\leq 30\) \(= 1\) -
7 \(\leq 10\) \(\leq 30\) \(= 0\) 所有的 \(a_i\) 相同
8 \(\leq 10\) \(\leq 30\) \(= 1\) -
9 \(\leq 15\) \(\leq 30\) \(= 0\) 所有的 \(a_i\) 相同
10 \(\leq 15\) \(\leq 30\) \(= 1\) -
11 \(\leq 30\) \(\leq 1000\) \(= 0\) 所有的 \(a_i\) 相同
12 \(\leq 30\) \(\leq 30\) \(= 0\) 所有的 \(a_i\) 相同
13 \(\leq 30\) \(\leq 1000\) \(= 0\) -
14 \(\leq 30\) \(\leq 1000\) \(= 1\) -
15 \(\leq 50\) \(\leq 1000\) \(= 0\) 所有的 \(a_i\) 相同
16 \(\leq 50\) \(\leq 30\) \(= 0\) 所有的 \(a_i\) 相同
17 \(\leq 50\) \(\leq 1000\) \(= 0\) -
18 \(\leq 50\) \(\leq 1000\) \(= 1\) -
19 \(\leq 100\) \(\leq 1000\) \(= 0\) -
20 \(\leq 100\) \(\leq 1000\) \(= 1\) -

SOLUTION

前置芝士——最大权闭合子图模型

有 \(n\) 个物品,第 \(i\) 个物品的价值为 \(w_i\)(可以为负),存在若干形如选 \(i\) 必须选 \(j\) 的限制。

最小割。

令超源 \(S\) 所在的集合为选择的物品集合,超汇 \(T\) 所在的集合为不选择的物品的集合

显然对于一个物品 \(i\) ,要么属于 \(S\) ,要么属于 \(T\)

对于价值为正的物品 \(i\),从 \(S\) 向 \(i\) 连边,边权为 \(w_i\)

表示不选择 \(i\) 会损失 \(wi\) 的收益。

对于价值为负或零的物品 \(i\),从 \(i\) 向 \(T\) 连边,边权为 \(−wi\) (显然 \(−wi>0\) )

表示选择了 \(i\) 要付出 \(−wi\) 的代价,即损失 \(−wi\) 的收益

对于选择 \(i\) 必选 \(j\) 的限制,由 \(i\) 向 \(j\) 连边,表示 \(j\) 必须属于 \(i\) 所在的集合

设最小割为 \(ans\),则最后所选择物品的收益最大值为 \(sum−ans\)

令 \(sum=∑_{w_i>0}w_i\)

其实最小割求的是最终不选的

对于\(value>0\)的割掉意味着不选,损失\(value\)

对于\(value<0\)的割掉意味着要选,损失\(-value\)

所以就是选出最小损失,然后\(答案=sum-ans\)

回归本题

然后我们考虑将题目解读成网络流最大权闭合子图

收费:

如果一共吃过了\(c\) 代号为\(x\) 的寿司,则需要为这些寿司付出\(mx^2+cx\)元钱

也就是每次吃一次代号为\(x\)的寿司,就会付\(x\)元钱

且但凡吃过代号为\(x\)的寿司,就会付\(mx^2\)元钱--------------\((1)\)

又由于最终求的是总美味度减去花费的总钱数的最大值

所以我们想到了将代号\(x\)的单独辟成一个点,每一段\([i,j]\)也是一个点\(id[i][j]\)

  • 对于\([i,i]\)\(id[i][i]\to T,flow=w[i][i]-a[i]\)\(id[i][j]\to idx[a[i]]\)

    第二个解释:就是\((1)\)那个地方,确保有代号\(x\)的寿司出现时,会有\(mx^2\)的花费

  • 对于\([i,j](j≠i)\)\(id[i][j]\to id[i+1][j]\)\(id[i][j]\to id[i][j-1]\)

    就是对应题目的注意在吃一次取的寿司时,不止一个综合美味度会被累加

  • 对于代号\(x\)\(idx[x]\to T,flow=mx^2\)

注意\(s\to id[i][j]\)的连边还是按照最大权闭合子图的模型连

这样我们就可以用最小割解决这个问题了

CODE

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=5e6+2,M=1e3+2,INF=1e18;
ll n,m,a[M],b[M],v[M][M],id[M][M],pos[M],t[M],S,T,tot,ans,sum;
ll cnt,hd[N],d[N]; 
struct node{ll to,nxt,w;}e[N];
void add(ll x,ll y,ll w){
	e[++cnt]={y,hd[x],w};
	hd[x]=cnt;
}
bool bfs(){
	queue<ll>q;
	memset(d,0,sizeof(d));
	q.push(S),d[S]=1;
	while(!q.empty()){
		ll x=q.front();q.pop();
		for(ll i=hd[x];~i;i=e[i].nxt){
			ll y=e[i].to,w=e[i].w;
			if(w&&!d[y]){
				d[y]=d[x]+1;
				if(y==T)return 1;
				q.push(y);
			}
		}
	}
	return 0;
}
ll dinic(ll x,ll flow){
	if(x==T)return flow;
	ll k,r=flow;
	for(ll i=hd[x];~i&&r;i=e[i].nxt){
		ll y=e[i].to,w=e[i].w;
		if(!w||d[y]!=d[x]+1)continue;
		k=dinic(y,min(r,w));
		if(!k)d[y]=0;
		e[i].w-=k;
		e[i^1].w+=k;
		r-=k;
		if(!r)return flow;
	}
	return	flow-r;
}
int main(){
	scanf("%lld%lld",&n,&m);
	memset(hd,-1,sizeof(hd));
	cnt=1;
	S=0;
	ll mx=0;
	for(ll i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		mx=max(mx,a[i]);
		for(ll j=i;j<=n;j++){
			id[i][j]=++tot;
		}
	} 
	T=mx+tot+1;
	for(ll i=1;i<=mx;i++){
		b[i]=tot+i;
		add(b[i],T,i*i*m),add(T,b[i],0);
	}
	for(ll i=1;i<=n;i++){
		for(ll j=i;j<=n;j++){
			scanf("%lld",&v[i][j]);
			if(i==j)v[i][i]-=a[i];
			if(v[i][j]>0){
				sum+=v[i][j];
				add(S,id[i][j],v[i][j]),add(id[i][j],S,0);
			}else{
				add(id[i][j],T,-v[i][j]),add(T,id[i][j],0);
			}
			if(i==j){
				add(id[i][j],b[a[i]],INF),add(b[a[i]],id[i][j],0);
			}else{
				add(id[i][j],id[i+1][j],INF),add(id[i+1][j],id[i][j],0);
				add(id[i][j],id[i][j-1],INF),add(id[i][j-1],id[i][j],0);
			}
		}
	}
	while(bfs()){
		ans+=dinic(S,INF);
	}
	printf("%lld",sum-ans);
	return 0;
}

完结撒花❀

★,°:.☆( ̄▽ ̄)/$:.°★

posted @ 2023-03-12 22:14  _Youngxy  阅读(63)  评论(0)    收藏  举报