[BZOJ 4873][SHOI&SXOI2017]寿司餐厅(最大权闭合子图)

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\n",ans);
    return 0;
}

 

posted @ 2017-05-03 00:56  Zars19  阅读(183)  评论(0编辑  收藏  举报