题面

传送门

思路

先看看这道题

修车

仔细理解一下,这两道题是不是一样的?

这道题的不同之处

但是有一个区别:本题中每一种车有多个需求,但是这个好办,连边的时候容量涨成$p\lbrack i\rbrack$就好了

但是还有一个区别:数据量变大了-_-

这直接导致了费用流裸做,TLE60分,因为有超过6e6条边

我们得想个办法改进一下

观察可得,这道题里,按照我们的模型,最多出现800条增广路,而且每次增广都是一的流量

也就是说我们实际上跑800次spfa即可

但是spfa和边唯一相关,我们全建好的图中6e6*800*k肯定会T

那我们就要想个办法优化边数

优化

我们观察发现,第一次spfa得出的最短路肯定是某人倒数第一个修某车某厨师倒数第一个做某菜,因为倒数第一个肯定比倒数第二个距离短

那么我们可以在一开始建图的时候,只把所有“倒数第一个做的菜”的那些边加上

一旦一条增广路被用掉了(也就是一个厨师-做菜顺序二元组$\left(j,k\right)$被用掉了),那么我们就把所有代表二元组$\left(j,k+1\right)$加上去(一共有n条),再跑spfa

这样我们图中的总边数不会超过$n\ast\sum_{i=1}^n p\lbrack i\rbrack$

也就是总时间在$O\left(np^2\ast k\right)$左右,k是spfa常数

这样就可以过了

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define inf 1e9
#define id(i,j) ((i-1)*p+j+n)
#define left(x) ((x-n-1)/p+1)
#define right(x) ((x-n-1)%p+1)
using namespace std;
inline int read(){
int re=0,flag=1;char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') flag=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
return re*flag;
}
int n,m,cnt=-1,first[100010],dis[100010],vis[100010],pre[100010],limit[100010];
struct edge{
int to,next,w,cap;
}a[10000010];
inline void add(int u,int v,int w,int cap){
a[++cnt]=(edge){v,first[u],w,cap};first[u]=cnt;
a[++cnt]=(edge){u,first[v],-w,0};first[v]=cnt;
}
int q[200010],ans,cost[50][110],p;
bool spfa(int s,int t){
int head=0,tail=1,u,v,w,i;
memset(dis,-1,sizeof(dis));memset(vis,0,sizeof(vis));
memset(pre,-1,sizeof(pre));memset(limit,0,sizeof(limit));
q[0]=s;vis[s]=1;dis[s]=0;limit[s]=inf;
while(head<tail){
u=q[head++];vis[u]=0;
for(i=first[u];~i;i=a[i].next){
v=a[i].to;w=a[i].w;
if(a[i].cap&&((dis[v]==-1)||(dis[v]>dis[u]+w))){
dis[v]=dis[u]+w;
pre[v]=i;limit[v]=min(limit[u],a[i].cap);
if(!vis[v]) q[tail++]=v,vis[v]=1;
}
}
}
return ~dis[t];
}
void mcmf(int s,int t){
int u,i;
while(spfa(s,t)){//这里最多sigma(p[i])次
for(u=t;~pre[u];u=a[pre[u]^1].to){
a[pre[u]].cap-=limit[t];a[pre[u]^1].cap+=limit[t];
ans+=limit[t]*a[pre[u]].w;
}//跑完一次更新答案
u=a[pre[t]^1].to;//u就是当前消耗的二元组,u+1就是下一个二元组
add(u+1,t,0,1);
for(i=1;i<=n;i++) add(i,u+1,cost[i][left(u+1)]*right(u+1),1);//加上对应的下一组边
}
}
int main(){
memset(first,-1,sizeof(first));int i,j,t1;
n=read();m=read();
for(i=1;i<=n;i++){
t1=read();p+=t1;
add(0,i,0,t1);
}
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
cost[i][j]=read();
add(i,id(j,1),cost[i][j],1);//初始边
}
}
for(j=1;j<=m;j++) add(id(j,1),n+p*m+1,0,1);
mcmf(0,n+p*m+1);
cout<<ans<<endl;
}

[NOI2012][bzoj2879] 美食节 [费用流+动态加边]的更多相关文章

  1. 【bzoj2879】[Noi2012]美食节 费用流+动态加边

    原文地址:http://www.cnblogs.com/GXZlegend 题目描述 CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节.作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴.他 ...

  2. [BZOJ2879] [Noi2012] 美食节 (费用流 & 动态加边)

    Description CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节.作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴.他很快就尝遍了美食节所有的美食.然而,尝鲜的欲望是难以满足的.尽 ...

  3. BZOJ 2879: [Noi2012]美食节( 费用流 + 动态加边 )

    倒着做菜..然后考虑为当前的人做菜对后面的人的影响就可以了..要动态加边 --------------------------------------------------------------- ...

  4. BZOJ 2879 美食节(费用流-动态加边)

    题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2879 题意:有n道菜,每道菜需要b[i]份,m个厨师,第j个厨师做第i道菜需要时间a[i ...

  5. [BZOJ1070] [SCOI2007] 修车 (费用流 & 动态加边)

    Description 同一时刻有N位车主带着他们的爱车来到了汽车维修中心.维修中心共有M位技术人员,不同的技术人员对不同的车进行维修所用的时间是不同的.现在需要安排这M位技术人员所维修的车及顺序,使 ...

  6. BZOJ 2879 [Noi2012]美食节 | 费用流 动态开点

    这道题就是"修车"的数据加强版--但是数据范围扩大了好多,应对方法是"动态开点". 首先先把"所有厨师做的倒数第一道菜"和所有菜连边,然后跑 ...

  7. [NOI2012]美食节——费用流(带权二分图匹配)+动态加边

    题目描述 小M发现,美食节共有n种不同的菜品.每次点餐,每个同学可以选择其中的一个菜品.总共有m个厨师来制作这些菜品.当所有的同学点餐结束后,菜品的制作任务就会分配给每个厨师.然后每个厨师就会同时开始 ...

  8. [NOI2012]美食节(费用流)

    题目描述 CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节.作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴.他很快就尝遍了美食节所有的美食.然而,尝鲜的欲望是难以满足的.尽管所有的菜品都 ...

  9. 【BZOJ 2879】[Noi2012]美食节 费用流

    思路同修车,就是多了一个骚气的操作:动态加边,我们通过spfa流的过程可以知道,我们一次只会跑一流量,最后一层边跑过就不会再悔改,所以说我们只会用到一大片里面的很少的点,所以我们如果可以动态加边的话我 ...

随机推荐

  1. Java代码工具箱之超出游标最大数

    1. Java大量写入oracle时容易出现此错.经过此错,也触动自己要深刻理解 java 的 prepareStatement 等对象,及数据库的连接与释放. 2. 原因:经常会出现在 for 循环 ...

  2. C# 常用函数和方法集汇总

    1.DateTime 数字型 System.DateTime currentTime=new System.DateTime(); 1.1 取当前年月日时分秒 currentTime=System.D ...

  3. Ubuntu 14.04 LTS 触摸板无法使用

    c16b上,触摸板不能使用,查找后发现,需要在加载驱动时增加参数. 如下所说: 1.使用以下命令后,触摸板可以使用 sudo modprobe -r psmouse sudo modprobe psm ...

  4. JSP出现"属性值[request.getParameter("myMessage")]引用["],在值内使用时必须被转义"的解决方法

    写JSP时出现属性值[request.getParameter("myMessage")]引用["],在值内使用时必须被转义. 源代码: <jsp:setPrope ...

  5. SummerVocation_Learning--java的String类方法总结

    壹: public char charAt(int index),返回字符串中第index个字符. public int length(), 返回字符串长度. public int indexOf(S ...

  6. numpy学习(二)

    ndarray的聚合操作 此博客讲的非常清楚,参照此博客即可 https://blog.csdn.net/qq_42571805/article/details/81146133

  7. Dialogue between Jack and Rose【jack 和 Rose的对话】

    Dialogue between Jack and Rose Rose : It's getting quiet. 越来越安静了 Jack : It's gonna take a couple of ...

  8. C++实例 MySTLString

    #include <iostream> #include <cstring> #include <string> using namespace std; clas ...

  9. CSAPP 缓冲区溢出试验

    缓冲区溢出试验是CSAPP课后试验之一,目的是: 更好的理解什么是缓冲区溢出 如何攻击带有缓冲区溢出漏洞的程序 如何编写出更加安全的代码 了解并理解编译器和操作系统为了让程序更加安全而提供的几种特性 ...

  10. 洛谷P2389 电脑班的裁员(区间DP)

    题目背景 隔壁的新初一电脑班刚考过一场试,又到了BlingBling的裁员时间,老师把这项工作交给了ZZY来进行.而ZZY最近忙着刷题,就把这重要的任务交(tui)给了你. 题目描述 ZZY有独特的裁 ...