P3749 [六省联考2017]寿司餐厅

题意

首先因为每个值只会被算一次且值的个数为\(n^2\)级别的,因此我们可以对每个\(d_{i,j}\)开一个点,之后就可以用最大权闭合子图做。

考虑题目的限制:

1.选择一个区间\([l,r]\)会将\(\sum\limits_{i=l}^{r}\sum\limits_{j=i+1}^rd_{i,j}\)选上:
我们显然不能全部连上,这是\(n^4\)级别的,我们可以只从\(d_{i,j}\)\(d_{i+1,j}\)\(d_{i,j-1}\)连边。

2.如果吃过\(c\)种代号为\(x\)的寿司,会付出\(m*x^2+c*x\)的代价:
拆成两部分:
<1>选了\(c\)种代号为\(x\)的寿司会付出\(c*x\)的代价,即选了\(d_{i,i}\)就必定要付出\(a_i\)的代价。
<2>如果选了代号为\(x\)的寿司,就会付出\(m*x^2\)的代价:
我们对每个\(a_i\)也开一个点,点权为\(-a_i^2\)

于是总结下连边是这样的:

对于\(d_{i,j}\)

如果\(i=j\)
\(d_{i,i}\)\(a_i\)连一条容量为\(inf\)的边,表示选了第\(i\)个就选了第\(a_i\)种。从\(d_{i,j}\)向汇点连一条容量为\(a_i\)的边,表示选了\(i\)号寿司需要付出\(a_i\)的代价。
反之:
\(d_{i,j}\)\(d_{i+1,j}\)\(d_{i,j-1}\)连容量为\(inf\)的边。
最后根据点权正负向源点和汇点连边。

code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=110;
const int inf=1e9;
int n,m,num,cnt=1,S,T,tot,ans;
int a[maxn],head[maxn*maxn+maxn],cur[maxn*maxn+maxn],dep[maxn*maxn+maxn];
int val[maxn][maxn],id[maxn][maxn];
struct edge{int to,nxt,flow;}e[(maxn*maxn+maxn)<<2];
inline void add(int u,int v,int w)
{
    e[++cnt].nxt=head[u];
    head[u]=cnt;
    e[cnt].to=v;
    e[cnt].flow=w;
}
inline bool bfs()
{
    memset(dep,0,sizeof(dep));
    for(int i=S;i<=T;i++)cur[i]=head[i];
    queue<int>q;
    q.push(S);dep[S]=1;
    while(!q.empty())
    {
        int x=q.front();q.pop();
        for(int i=head[x];i;i=e[i].nxt)
        {
            int y=e[i].to;
            if(dep[y]||e[i].flow<=0)continue;
            dep[y]=dep[x]+1;q.push(y);
        }
    }
    return dep[T]>0;
}
int dfs(int x,int goal,int lim)
{
    if(x==goal||lim<=0)return lim;
    int res=lim;
    for(int i=cur[x];i;i=e[i].nxt)
    {
        cur[x]=i;
        int y=e[i].to;
        if(dep[y]!=dep[x]+1||e[i].flow<=0)continue;
        int tmp=dfs(y,goal,min(res,e[i].flow));
        if(tmp<=0)dep[y]=0;
        res-=tmp;
        e[i].flow-=tmp,e[i^1].flow+=tmp;
        if(res<=0)break;
    }
    return lim-res;
}
inline int Dinic()
{
    int res=0;
    while(bfs())res+=dfs(S,T,inf);
    return res;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),num=max(num,a[i]);
    for(int i=1;i<=n;i++)
        for(int j=i;j<=n;j++)
            scanf("%d",&val[i][j]),id[i][j]=++tot;
    S=0,T=tot+num+1;
    for(int i=1;i<=n;i++)
        for(int j=i;j<=n;j++)
        {
            int w=val[i][j];
            if(i==j)
            {
                if(m)add(id[i][j],tot+a[i],inf),add(tot+a[i],id[i][j],0);
                w-=a[i];
            }
            else 
            {
                add(id[i][j],id[i+1][j],inf),add(id[i+1][j],id[i][j],0);
                add(id[i][j],id[i][j-1],inf),add(id[i][j-1],id[i][j],0);
            }
            if(w>=0)ans+=w,add(S,id[i][j],w),add(id[i][j],S,0);
            else add(id[i][j],T,-w),add(T,id[i][j],0);
        }
    if(m)
        for(int i=1;i<=num;i++)
            add(tot+i,T,i*i),add(T,tot,0);
    printf("%d",ans-Dinic());
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/nofind/p/12088404.html