Description

Flappy Bird是一款风靡一时的休闲手机游戏。玩家需要不断控制点击手机屏幕的频率来调节小鸟的飞行高度,让小鸟顺利通过画面右方的管道缝隙。如果小鸟一不小心撞到了水管或者掉在地上的话,便宣告失败。
现在小鸟们遇到了一个难题,他们遇到了一堵巨大的墙,墙上仅有m个洞供他们通过,由于小鸟们的体型不同且墙上洞的形状也不同,所以每种体型的鸟通过每个洞的时间都不同,鸟的体型共有n种,第i种体型的鸟通过第j个洞需要的时间记为T(i,j),且一个洞必须前一只鸟通过之后后一只鸟才能开始通过。
从时刻0开始,鸟开始通过,而每一只鸟的等待时间为从时刻0到自己已经通过洞的时间。现在知道了第i种体型的鸟有pi只,请求出使所有鸟都通过墙的最少的等待时间之和。

Solution

看上去就像费用流,但是由于标着个NOIP就没敢打。结果爆零。
首先很显然的是S向每种鸟连一条费用为0,容量为p[i]的边。
设鸟的个数有pp个。
如果一个洞j被一只鸟i最后一次到达,那么会对答案造成t[i][j]的影响,倒数第二个到达会造成t[i][j]*2的影响因为最后一个到达的还要多等t[i][j]的时间……
那么可以把每个洞拆成pp个点,每种鸟i都向洞j拆成的倒数第k次到达的点(拆成的第k个点)连一条费用为t[i][j]*k,容量为1的边。
然后每个洞拆成的所有点都向T连一条费用为0,容量为1的边。
然后跑一次最小费用最大流就好了。两种方法:1、​​zkw​​;2、spfa(这道题spfa比较快)

边数太多!!!

一共有100个洞,800只鸟,会拆除80000个点,40种鸟向80000个点连边会连出3200000条边,跑一跑绝对会烂掉。
考虑动态加边。
一开始每个洞只拆成一个点(注意这时向T的连边也只有这些点),然后跑一次增广,找到增广的那条路径填上的洞,然后这个洞再多拆出一个点,n中鸟向它建边,它向T连边。
这样动态加边时间就会快很多。

为什么NOI的题会被表上NOIP给我们做

​​NOI2012美食节​​

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define rep(i,a) for(i=first[a];i;i=next[i])
using namespace std;
const int maxn=500007;
int i,j,k,l,n,m,ans;
int p[maxn],t[1007][1007],S,T,o,hou;
int first[maxn*2],next[maxn*2],last[maxn*2],chang[maxn*2],fan[maxn*2],cost[maxn*2],num,pp;
int bz[maxn],d[maxn],id,now[maxn],sh[maxn],data[maxn],ci;
void add(int x,int y,int z,int o){
last[++num]=y;next[num]=first[x];first[x]=num;chang[num]=z;cost[num]=o;fan[num]=num+1;
last[++num]=x;next[num]=first[y];first[y]=num;chang[num]=0;cost[num]=-o;fan[num]=num-1;
}
bool spfa(){
int head,tail,now,i,j,p,q;
fo(i,S,T)d[i]=0x7fffffff;q=d[0];
bz[0]=1;d[0]=0;
data[1]=0;
head=0;tail=1;
while(head<tail){
now=data[++head];
i=first[now];
while(i!=0){
if(chang[i]&&d[last[i]]>d[now]+cost[i]){
d[last[i]]=d[now]+cost[i];
sh[last[i]]=i;
if (!bz[last[i]]){
data[++tail]=last[i];
bz[last[i]]=1;
}
}
i=next[i];
}
bz[now]=0;
}
if(d[T]!=q)return 1;return 0;
}
void zeng(){
int i=sh[T],j=0x7fffffff,k=0;
for(;i;i=sh[last[fan[i]]])
j=min(j,chang[i]),k+=cost[i],chang[i]-=j,chang[fan[i]]+=j;;
ans+=j*k;
// for(i=sh[T];i;i=sh[last[fan[i]]])

int hou=last[fan[sh[T]]]-n;
if(hou%pp){
hou++;
int a=(hou-1)/pp+1,b=((hou-1)%pp+1);
add(n+hou,T,1,0);
fo(i,1,n){
int v=t[i][a]*b;
add(i,n+hou,1,v);
}
}
}
int main(){
// freopen("fan.in","r",stdin);
scanf("%d%d",&n,&m);
fo(i,1,n){
scanf("%d",&p[i]);
pp+=p[i];
add(S,i,p[i],0);
}
S=0,T=pp*m+n+1;
fo(i,1,n){
fo(j,1,m){
scanf("%d",&t[i][j]);
}
}
fo(i,1,n)fo(j,1,m)add(i,n+(j-1)*pp+1,1,t[i][j]);
o=0;
fo(j,1,m)add(n+(j-1)*pp+1,T,1,0);
while(spfa())zeng();
printf("%d\n",ans);
}