【最大权闭合子图】bzoj4873 [Shoi2017]寿司餐厅
4873: [Shoi2017]寿司餐厅
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 369 Solved: 256
[Submit][Status][Discuss]
Description
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想知道,在这家餐厅吃寿司,自己能获得的总美
味度(包括所有吃掉的单种寿司的美味度和所有被累加的综合美味度)减去花费的总钱数的最大值是多少。由于她
不会算,所以希望由你告诉她
Input
第一行包含两个正整数n,m,分别表示这家餐厅提供的寿司总数和计算寿司价格中使用的常数。
第二行包含n个正整数,其中第k个数ak表示第k份寿司的代号。
接下来n行,第i行包含n-i+1个整数,其中第j个数di,i+j-1表示吃掉寿司能
获得的相应的美味度,具体含义见问题描述。
N<=100,Ai<=1000
Output
输出共一行包含一个正整数,表示Kiana能获得的总美味度减去花费的总钱数的最大值。
Sample Input
3 1
2 3 2
5 -10 15
-10 15
15
2 3 2
5 -10 15
-10 15
15
Sample Output
12
题解
首先这道题我们可以发现,当我们选择了区间[L,R]时,区间[L+1,R]和[L,R-1]也被选择
这样我们就发现,这题其实是在求最大权闭合子图
于是我们对于[L,R](L!=R)向[L+1,R]和[L,R-1]连边
对于[L,R](L==R),我们连边的权值为收益-a[L]
再对每种收益连好边,具体连边请看代码
代码
//by 减维 #include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<bitset> #include<set> #include<cmath> #include<vector> #include<set> #include<map> #include<ctime> #include<algorithm> #define ll long long #define il inline #define rg register #define db double #define inf 1<<30 #define maxn 11005 #define eps 1e-8 using namespace std; inline int read() { int ret=0;bool fla=0;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-'){fla=1;ch=getchar();} while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();} return fla?-ret:ret; } struct edge{ int to,ne,cap; }e[maxn<<7]; int n,m,s,t,tot,ans,ecnt=1,a[105],num[105][105],val[105][105],head[maxn],layer[maxn],cur[maxn]; void add(int x,int y,int z) { e[++ecnt]=(edge){y,head[x],z};head[x]=ecnt; e[++ecnt]=(edge){x,head[y],0};head[y]=ecnt; } bool bfs() { memset(layer,0,sizeof layer);layer[s]=1; queue<int>q;q.push(s); while(!q.empty()) { int d=q.front();q.pop(); for(int i=head[d];i;i=e[i].ne) { int dd=e[i].to; if(e[i].cap&&!layer[dd]) { layer[dd]=layer[d]+1; if(dd==t) return 1; q.push(dd); } } } return 0; } int dfs(int x,int cap) { if(x==t) return cap; int tmp,ret=0; for(int &i=cur[x];i;i=e[i].ne) { int dd=e[i].to; if(e[i].cap&&layer[dd]==layer[x]+1) { tmp=dfs(dd,min(cap,e[i].cap)); ret+=tmp;cap-=tmp; e[i].cap-=tmp;e[i^1].cap+=tmp; if(cap==0) return ret; } } if(!ret) layer[x]=0; return ret; } void dinic() { while(bfs()) { for(int i=s;i<=tot;++i) cur[i]=head[i]; ans-=dfs(s,inf); } } int main() { n=read(),m=read(); for(int i=1;i<=n;++i) a[i]=read(); s=0,t=1001,tot=1001; for(int i=1;i<=1000;++i) add(i,t,m*i*i); for(int i=1;i<=n;++i) for(int j=i;j<=n;++j) num[i][j]=++tot,val[i][j]=read(); for(int i=1;i<=n;++i) for(int j=i;j<=n;++j) { int x=val[i][j]; if(j==i) x-=a[i],add(num[i][j],a[i],inf); if(x>0) add(s,num[i][j],x),ans+=x; else add(num[i][j],t,-x); if(j!=1) add(num[i][j],num[i][j-1],inf),add(num[i][j],num[i+1][j],inf); } dinic(); printf("%d",ans); return 0; }