【最大权闭合子图】bzoj4873 [Shoi2017]寿司餐厅

4873: [Shoi2017]寿司餐厅

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 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

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;
}

 

posted @ 2018-01-08 15:55  减维  阅读(155)  评论(0编辑  收藏  举报