发现每一种寿司的组合方式都只能被计算一次,并且存在一些依赖关系:比如说选择了
[
l
,
r
]
[l,r]
[l,r]就必须选择它的所有子区间。
这启发我们利用最大权闭合子图来解决问题。不妨考虑将所有区间看成点,按照最大权闭合子图的方式来建图。当然,对于区间
[
l
,
r
]
[l,r]
[l,r]我们不一定要连向所有的子区间,只要连向
[
l
,
r
−
1
]
[l,r-1]
[l,r−1]和
[
l
+
1
,
r
]
[l+1,r]
[l+1,r]就可以了。
然后考虑如何处理
m
x
2
+
c
x
mx^2+cx
mx2+cx的情况。可以对于每一种
x
x
x建出一个点,然后将所有
a
[
i
]
=
x
a[i]=x
a[i]=x的点
[
i
,
i
]
[i,i]
[i,i]连向
x
x
x,然后
x
x
x向
T
T
T连一条容量为
m
x
2
mx^2
mx2的边,这就处理完了
m
x
2
mx^2
mx2的问题。然后处理
c
x
cx
cx,只要将所有
a
[
i
]
=
x
a[i]=x
a[i]=x的点向
T
T
T连一条容量为
x
x
x的边就可以了。
考虑到这是一个最小割问题,所以算法显然是正确的。
代码
#include<bits/stdc++.h>#define ll long longusingnamespace std;template<typename T>voidread(T &x){
x =0;int f =1;char c =getchar();while(!isdigit(c)){if(c =='-') f =-1; c =getchar();}while(isdigit(c)) x = x *10+ c -'0', c =getchar(); x *= f;}constint N =110, M = N * N, inf =1<<30;int cnt, a[N], b[N], d[N][N], num[N][N], l[M], cur[M], head[M];struct Edge {int next, num, c;} e[N * N * N];voidadd(int x,int y,int c){e[++cnt]=(Edge){head[x], y, c}, head[x]= cnt;}voidAdd(int x,int y,int c){add(x, y, c),add(y, x,0);}boolbfs(int s,int t){for(int i = s; i <= t; i++) l[i]=-1;
queue <int> q; q.push(s), l[s]=0;while(!q.empty()){int x = q.front(); q.pop();for(int p = head[x]; p; p = e[p].next){int y = e[p].num, c = e[p].c;if(c && l[y]==-1) q.push(y), l[y]= l[x]+1;}}return l[t]!=-1;}intdfs(int x,int t,int lim){if(x == t)return lim;int ret =0;for(int&p = cur[x]; p; p = e[p].next){int y = e[p].num, c = e[p].c;if(l[y]== l[x]+1&& c){int w =dfs(y, t,min(lim - ret, c));
e[p].c -= w, e[p ^1].c += w, ret += w;if(ret == lim)return ret;}}if(!ret) l[x]=-1;return ret;}intdinic(int s,int t){int ret =0;while(bfs(s, t)){memcpy(cur, head,sizeof(cur));
ret +=dfs(s, t, inf);}return ret;}intmain(){int n, m, tot =0;read(n),read(m); cnt =1;for(int i =1; i <= n; i++)read(a[i]);for(int i =1; i <= n; i++)for(int j = i; j <= n; j++)read(d[i][j]), num[i][j]=++tot;int s =0, t = n + tot +1; ll ans =0;for(int i =1; i <= n; i++)for(int j = i; j <= n; j++){if(d[i][j]>0) ans += d[i][j],Add(s, num[i][j], d[i][j]);elseAdd(num[i][j], t,-d[i][j]);if(i < j)Add(num[i][j], num[i][j -1], inf),Add(num[i][j], num[i +1][j], inf);}
map <int,int> h; h.clear();for(int i =1; i <= n; i++)if(h.count(a[i])) b[i]= h[a[i]];else b[i]= h[a[i]]=++tot,Add(tot, t, m * a[i]* a[i]);for(int i =1; i <= n; i++)Add(num[i][i], b[i], inf),Add(num[i][i], t, a[i]);
cout << ans -dinic(s, t)<<"\n";return0;}