您的位置:首页 > 其它

bzoj4873: [Shoi2017]寿司餐厅

2017-09-11 18:06 417 查看
bzoj4873: [Shoi2017]寿司餐厅

题意

有n(<=100)种寿司,可以选择任意段连续的寿司,每次得到的美味度是该段所有子区间美味度之和。如果选取了第i种寿司,需要付出代价Ai。对于一个区间,无论被取多少次,其美味度只计算一次。花费同理。当m(一个常数)=1时,如果有代号为x的寿司被选取,需额外花费m*x²。

背景

插一段本来应该在jloi2017滚粗记里的东西

省选题,当时一直调不过样例,最后十分钟发现自己看错题了orz

于是赶快写暴力,出来发现得了5分…是六道题里面得分最低的…严重拉分导致挂了day2

暑假的时候在过网络流专题,想着什么时候把这道题过掉…于是抽了个空写了下中途又和当时一样看错题了 题面里说的是c种不是c个啊 单个寿司的花费不重复计算啊

题解

最大权闭合子图…知道算法就很好yy了

我们认为与S连通的被选,与T连通的不被选。

对于每个区间[l,r]建点,dij>0时,S向[l,r]连dij,否则[l,r]向T连-dij。

选取一个区间[l,r] (l< r)时,一定要选的是[l+1,r]和[l,r-1]。于是[l,r]向这两个区间对应的点连inf。对于[l,l]对应的点,向T连Al,表示花费。

在m=1时,对于每种存在的Ai建点,向T连Ai²;对于代号为Ai的寿司, 向Ai对应的点连inf。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 101
#define V 5200
#define M 32000
#define inf 0x3f3f3f3f
using namespace std;
int n,a
,b[1001],c

,d

,tot=1;
bool m;
int to[M],hd[M],val[M],lk[V],cnt=1,ans;
void add(int u,int v,int w)
{
to[++cnt]=v,hd[cnt]=lk[u],val[cnt]=w,lk[u]=cnt;
to[++cnt]=u,hd[cnt]=lk[v],lk[v]=cnt;
}
int q[V],h,t,dis[V],cur[V];
bool bfs()
{
for(int i=0;i<=tot;i++)
dis[i]=0,cur[i]=lk[i];
h=q[0]=0,t=dis[0]=1;
int x;
while(h<t)
{
x=q[h++];
for(int k,i=lk[x];i;i=hd[i])
if(val[i]&&!dis[k=to[i]])
dis[q[t++]=k]=dis[x]+1;
}
return dis[1];
}
int dfs(int x,int v)
{
if(x==1)return v;
int cst,ret=0;
for(int k,&i=cur[x];i;i=hd[i])
if(val[i]&&dis[k=to[i]]==dis[x]+1)
{
cst=dfs(k,v<val[i]?v:val[i]);
v-=cst,ret+=cst,val[i]-=cst,val[i^1]+=cst;
if(!v)break;
}
if(!ret)dis[x]=-1;
return ret;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",a+i);
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++)
scanf("%d",d[i]+j);
for(int i=0;i<n;i++)
for(int j=1;j<=n-i;j++)
{
c[j][j+i]=++tot;
if(d[j][j+i]>0)
add(0,tot,d[j][j+i]),ans+=d[j][j+i];
else add(tot,1,-d[j][j+i]);
if(i)add(tot,c[j+1][j+i],inf),add(tot,c[j][j+i-1],inf);
else
{
if(m)
{
if(!b[a[j]])b[a[j]]=++tot,add(tot,1,a[j]*a[j]);
add(c[j][j],b[a[j]],inf);
}
add(c[j][j],1,a[j]);
}
}
while(bfs())ans-=dfs(0,inf);
printf("%d",ans);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: