Bzoj2879 [NOI2012] 美食节

Description

https://www.luogu.com.cn/problem/P2050

Solution

普通的网络流很没前途啊!给每个厨师新建一个点表示倒数第几个做的菜,然后向每个菜连边。这样点数已经 $O(mp)$ 了。

考虑动态加点的网络流,先新建一个点表示倒数第一个做的菜,然后记录增光路上的一个新的点,给这个点新开一个点表示这个厨师倒数第二个做的菜。这样做下去直到不能增广。这样每次增广只会增加一个点,点数 $n+2m+p$。

我的写法不知道为什么必须要在已经增广的流量 $=p$ 时手动退出,不然一定会有负环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define maxn 1105
#define put() putchar('\n')
#define Tp template<typename T>
#define Ts template<typename T,typename... Ar>
using namespace std;
Tp void read(T &x){
int f=1;x=0;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();}
x*=f;
}
namespace Debug{
Tp void _debug(char* f,T t){cerr<<f<<'='<<t<<endl;}
Ts void _debug(char* f,T x,Ar... y){while(*f!=',') cerr<<*f++;cerr<<'='<<x<<",";_debug(f+1,y...);}
#define gdb(...) _debug((char*)#__VA_ARGS__,__VA_ARGS__)
}using namespace Debug;
#define fi first
#define se second
#define mk make_pair
const int mod=1e9+7;
const int inf=1e15;
int power(int x,int y=mod-2) {
int sum=1;
while (y) {
if (y&1) sum=sum*x%mod;
x=x*x%mod;y>>=1;
}
return sum;
}
int head=1,h[maxn];
struct yyy {
int to,z,w,val;
void add(int x,int y,int W,int V) {
to=y;z=h[x];h[x]=head;w=W;val=V;
}
}a[2000005];
int dis[maxn],pre[maxn],fl[maxn];
void ins(int x,int y,int z,int val) {
// gdb(x,y,z,val);
a[++head].add(x,y,z,val);
a[++head].add(y,x,0,-val);
}
int s,t,n,m,sum,ans,tot[maxn],vis[maxn],nex[maxn];
queue<int>q;
bool bfs(void) {
int i,x;
memset(tot,0,sizeof(tot));
memset(dis,0x3f,sizeof(dis));
fl[s]=inf,dis[s]=0;q.push(s);
while (!q.empty()) {
x=q.front();q.pop();++tot[x];vis[x]=0;
if (tot[x]>t) return gdb(tot[x]),0;//判负环???
// 去掉这行以后样例都跑不出来
// gdb(x,dis[x],fl[x]);
for (i=h[x];i;i=a[i].z) if (a[i].w&&dis[a[i].to]>dis[x]+a[i].val) {
fl[a[i].to]=min(fl[x],a[i].w);
dis[a[i].to]=dis[x]+a[i].val;
pre[a[i].to]=i;
if (!vis[a[i].to]) vis[a[i].to]=1,q.push(a[i].to);
}
}
if (dis[t]>=inf) return 0;
int now=t,res=fl[t];
// gdb(res,dis[t]);
ans+=res*dis[t];
while (now^s) {
a[pre[now]].w-=res,a[pre[now]^1].w+=res;
nex[a[pre[now]^1].to]=now;
now=a[pre[now]^1].to;
}
return 1;
}
int p[maxn],c[105][105],nums[maxn],id[maxn];
signed main(void){
freopen("1.in","r",stdin);
int i,j;
read(n);read(m);
for (i=1;i<=n;i++) read(p[i]),sum+=p[i];
for (i=1;i<=n;i++) for (j=1;j<=m;j++) read(c[j][i]);
int cnt=n+m;s=0;t=n+m*2+sum;
for (i=1;i<=n;i++) ins(m+i,t,p[i],0);
for (i=1;i<=m;i++) ins(s,i,inf,0);
for (i=1;i<=m;i++) {
++cnt;++nums[i];
ins(i,cnt,1,0);
for (j=1;j<=n;j++) {
ins(cnt,j+m,1,c[i][j]*nums[i]);
}
}
int times=0;
while (bfs()) {
int now=t;
++times;
if (times==sum) break;//手动退出
i=nex[s];
// ins(s,i,1,0);
++cnt;++nums[i];
ins(i,cnt,1,0);
for (j=1;j<=n;j++)
ins(cnt,j+m,1,c[i][j]*nums[i]);
}
printf("%lld\n",ans);
return 0;
}
//i=begin && g++ $i.cpp -o $i -std=c++14 -O2 && ./$i