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想知道,在这家餐厅吃寿司,自己能获得的总美
味度(包括所有吃掉的单种寿司的美味度和所有被累加的综合美味度)减去花费的总钱数的最大值是多少。由于她
不会算,所以希望由你告诉她
Solution
最大权闭合子图
(考试的时候没有想到这种神奇的依赖关系和建图方式)
区间[i,j]需要向[i+1,j]和[i,j-1]连边,INF
区间[i,i]向点i连边,INF
每个区间如果d[i][j]为正由s向它连容量为d[i][j]的边
为负则向t连容量为-d[i][j]的边
每个点i向t连容量a[i]的边
为了那个“mx^2+cx元钱”还需要每个点i向它的编号a[i]连容量为INF的边,然后每个a[i]向t连m*a[i]*a[i]的边
↑当时被这个条件吓到了…现在发现并不是想的那样2333
#include<iostream> #include<cstdlib> #include<cstdio> #include<cstring> #include<queue> #define INF 0x3f3f3f3f using namespace std; int read() { int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){ if(c=='-')f=-1;c=getchar(); } while(c>='0'&&c<='9'){ x=x*10+c-'0';c=getchar(); } return x*f; } int s,t,n,m,a[105],dot[105][105],id,key[1005]; int head[50005],cnt=0,level[50005]; struct Node { int next,to,cap; }Edges[1000005]; void addedge(int u,int v,int c) { Edges[cnt].next=head[u]; head[u]=cnt; Edges[cnt].to=v; Edges[cnt++].cap=c; } void insert(int u,int v,int c) { addedge(u,v,c); addedge(v,u,0); } queue<int>q; bool bfs() { memset(level,-1,sizeof(level)); level[s]=0;q.push(s); while(!q.empty()) { int u=q.front();q.pop(); for(int i=head[u];~i;i=Edges[i].next) { int v=Edges[i].to; if(level[v]==-1&&Edges[i].cap) level[v]=level[u]+1,q.push(v); } } if(level[t]==-1)return false; return true; } int dfs(int u,int flow) { if(u==t)return flow; int f=0,d=0; for(int i=head[u];~i&&f<flow;i=Edges[i].next) { int v=Edges[i].to; if(level[v]==level[u]+1&&Edges[i].cap) { d=dfs(v,min(flow-f,Edges[i].cap)); f+=d; Edges[i].cap-=d; Edges[i^1].cap+=d; } } if(!f)level[u]=-1; return f; } int main() { memset(head,-1,sizeof(head)); n=read(),m=read(); s=0,t=n*n+2*n+1;id=n+1; for(int i=1;i<=n;i++) a[i]=read(); int ans=0; for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) ++id,dot[i][j]=id; for(int i=1;i<=n;i++) { for(int j=i;j<=n;j++) { int x=read(); if(x>0)insert(s,dot[i][j],x),ans+=x; else insert(dot[i][j],t,-x); if(i!=j) { insert(dot[i][j],dot[i+1][j],INF); insert(dot[i][j],dot[i][j-1],INF); } else insert(dot[i][j],i,INF); } insert(i,t,a[i]); if(!key[a[i]])++id,key[a[i]]=id,insert(id,t,m*a[i]*a[i]); insert(i,key[a[i]],INF); } int d=0; while(bfs()) { while(d=dfs(s,INF)) ans-=d; } printf("%d ",ans); return 0; }