来自FallDream的博客,未经允许,请勿转载,谢谢。
Kiana最近喜欢到一家非常美味的寿司餐厅用餐。每天晚上,这家餐厅都会按顺序提供n种寿司,第i种寿司有一个代号ai和美味度di,i,不同种类的寿司有可能使用相同的代号。每种寿司的份数都是无限的,Kiana也可以无限次取寿司来吃,但每种寿司每次只能取一份,且每次取走的寿司必须是按餐厅提供寿司的顺序连续的一段,即Kiana可以一次取走第1,2种寿司各一份,也可以一次取走第2,3种寿司各一份,但不可以一次取走第1,3种寿司。由于餐厅提供的寿司种类繁多,而不同种类的寿司之间相互会有影响:三文鱼寿司和鱿鱼寿司一起吃或许会很棒,但和水果寿司一起吃就可能会肚子痛。因此,Kiana定义了一个综合美味度di,j(i<j),表示在一次取的寿司中,如果包含了餐厅提供的从第i份到第j份的所有寿司,吃掉这次取的所有寿司后将获得的额外美味度。由于取寿司需要花费一些时间,所以我们认为分两次取来的寿司之间相互不会影响。注意在吃一次取的寿司时,不止一个综合美味度会被累加,比如若Kiana一次取走了第1,2,3种寿司各一份,除了d1,3以外,d1,2,d2,3也会被累加进总美味度中。神奇的是,Kiana的美食评判标准是有记忆性的,无论是单种寿司的美味度,还是多种寿司组合起来的综合美味度,在计入Kiana的总美味度时都只会被累加一次。比如,若Kiana某一次取走了第1,2种寿司各一份,另一次取走了第2,3种寿司各一份,那么这两次取寿司的总美味度d1,1+d2,2+d3,3+d1,2+d2,3,其中d2,2只会计算一次。奇怪的是,这家寿司餐厅的收费标准很不同寻常。具体来说,如果Kiana一共吃过了c(c>0)种代号为x的寿司,则她需要为这些寿司付出mx^2+cx元钱,其中m是餐厅给出的一个常数。现在Kiana想知道,在这家餐厅吃寿司,自己能获得的总美味度(包括所有吃掉的单种寿司的美味度和所有被累加的综合美味度)减去花费的总钱数的最大值是多少。由于她不会算,所以希望由你告诉她。
n<=100 ai<=1000
貌似是叫最大权闭合子图
也就是如果你选了[i,j],你就必须选[i,j-1]和[i+1,j]
考虑最小割,先对于每一个编号建出一个点,从它向T连编号平方*m的边。
对于区间[i,j]
如果ij不等,先向[i,j-1][i+1,j]两个点连INF的边,然后如果点权正数就让答案加上它并且从S向它连点权的边,否则向T连点权的边。
如果ij相等,它的点权减去ai ,并且向它的编号对应节点连INF的边;和S,T的建边方法相同
然后最小割。
#include<iostream>
#include<cstdio>
#include<cstring>
#define S 0
#define MN 11001
#define INF (ll)1e18
#define num(x,y) (x-1)*n+y
#define ll long long
using namespace std;
inline int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-') f=-;ch=getchar();}
while(ch>=''&&ch<='') x=x*+ch-'', ch=getchar();
return x*f;
} ll ans=;
int head[MN+],c[MN+],d[MN+],T,top,a[],q[MN+],cnt=,n,m,s[][];
struct edge{int to,next;ll w;}e[MN*]; inline void ins(int f,int t,ll w)
{
e[++cnt]=(edge){t,head[f],w};head[f]=cnt;
e[++cnt]=(edge){f,head[t],};head[t]=cnt;
} bool bfs()
{
memset(d,,sizeof(int)*(T+));int i,j;
for(d[q[top=i=]=S]=;i<=top;++i)
for(int j=c[q[i]]=head[q[i]];j;j=e[j].next)
if(e[j].w&&!d[e[j].to])
d[q[++top]=e[j].to]=d[q[i]]+;
return d[T];
} ll dfs(int x,ll f)
{
if(x==T) return f;ll used=;
for(int&i=c[x];i;i=e[i].next)
if(e[i].w&&d[e[i].to]==d[x]+)
{
int w=dfs(e[i].to,min(f-used,e[i].w));
used+=w;e[i].w-=w;e[i^].w+=w;
if(used==f) return f;
}
return d[x]=-,used;
} int main()
{
n=read();m=read();T=n*n+;
for(int i=;i<=;++i) ins(n*n+i,T,1LL*i*i*m);
for(int i=;i<=n;++i) a[i]=read();
for(int i=;i<=n;++i)
for(int j=i;j<=n;++j)
{
s[i][j]=read();
if(i!=j) ins(num(i,j),num(i+,j),INF),
ins(num(i,j),num(i,j-),INF);
else s[i][j]-=a[i],ins(num(i,j),n*n+a[i],INF);
if(s[i][j]>) ans+=s[i][j],ins(S,num(i,j),s[i][j]);
if(s[i][j]<) ins(num(i,j),T,-s[i][j]);
}
while(bfs()) ans-=dfs(S,INF);
printf("%lld\n",ans);
return ;
}